简单数论$+$状压$+$数位$DP$
$1-9$的最小公倍数是$2520$,如果开两维$2520$分别表示各位的$lcm$以及当前模$2520$的余数显然会爆空间。
容易发现$1-9$取某些数的$lcm$一定是$2520$的约数,而$2520$的约数不多,只有50个左右,于是把无用的值去掉,把表示出现数字$lcm$的维度压到$50$,即可$AC$。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using i64 = long long;
int mp[2521], val[51];
int nums[21], len;
i64 f[21][2521][55];
int __lcm(int a, int b) {
return a * b / __gcd(a, b);
}
i64 dfs(int pos, int mod, int lcm, int lim) {
if (!pos) return mod % val[lcm] == 0;
if (!lim && ~f[pos][mod][lcm]) return f[pos][mod][lcm];
int up = lim ? nums[pos] : 9;
i64 res = 0;
for (int i = 0; i <= up; i++)
res += dfs(pos - 1, (mod * 10 + i) % 2520, i ? mp[__lcm(val[lcm], i)] : lcm, lim & i == up);
return lim ? res : f[pos][mod][lcm] = res;
}
i64 dp(i64 x) {
len = 0;
while (x) nums[++len] = x % 10, x /= 10;
return dfs(len, 0, 1, 1);
}
signed main() {
int T;
cin >> T;
memset(f, -1, sizeof f);
int idx = 0;
for (int i = 1; i <= 2520; i++)
if (2520 % i == 0)
mp[i] = ++idx, val[idx] = i;
while (T--) {
i64 l, r;
cin >> l >> r;
if (l > r) swap(l, r);
cout << dp(r) - dp(l - 1) << endl;
}
return 0;
}