题意:求在给定区间内满足能被自身的所有非零位整除的数的个数。
1,2,3,4,5,6,7,8,9的最小公倍数为2520,1到9内任意数字组合的最小公倍数都是2520的因子,那我们在dfs的时候记录目前数模上2520的值pre,同时记录已选数的最小公倍数,到递归结束的时候判断pre模上所有已选数的最小公倍数是否为零。
还有一点是:事先把所有的公倍数预处理出来。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lng long long
using namespace std;
const int maxn = 3000;
lng dp[19][50][2550];
lng a, b;
int num[20], tot;
int s[50], s1[maxn];
int l[50][50];
int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); }
void init()
{
int lcm, c = 0;
for(int x1 = 0; x1 < 4; ++x1)
{
for(int x2 = 0; x2 < 3; ++x2)
{
for(int x3 = 0; x3 < 2; ++x3)
{
for(int x4 = 0; x4 < 2; ++x4)
{
int tmp = 0;
lcm = 1 << x1;
while(tmp < x2) { lcm *= 3; tmp++; }
lcm *= x3 ? 5 : 1; lcm *= x4 ? 7 : 1;
s[c] = lcm;
s1[lcm] = c++;
}
}
}
}
for(int i = 0; i < 48; ++i)
{
for(int j = i; j < 48; ++j)
{
int tmp = s[i] * s[j] / gcd(s[i], s[j]);
l[i][j] = l[j][i] = s1[tmp];
}
}
}
lng dfs(int pos, int sta, int pre, int ok)
{
if(pos == -1) return pre % s[sta] == 0;
if(!ok && dp[pos][sta][pre] != -1) return dp[pos][sta][pre];
lng ans = dfs(pos - 1, sta, (pre * 10) % 2520, ok && num[pos] == 0);
int end = ok ? num[pos] : 9;
for(int i = 1; i <= end; ++i)
{
ans += dfs(pos - 1, l[sta][s1[i]], ((pre * 10) + i) % 2520, ok && i == end);
}
if(!ok) dp[pos][sta][pre] = ans;
return ans;
}
lng calc(lng n)
{
tot = 0;
while(n) { num[tot++] = n % 10; n /= 10; }
lng ans = dfs(tot - 1, 0, 0, 1);
return ans;
}
void prework()
{
cin >> a >> b;
}
void solve()
{
cout << calc(b) - calc(a - 1) << "\n";
}
int main()
{
memset(dp, 0xff, sizeof(dp));
init();
freopen("in.txt", "r", stdin);
int t; cin >> t;
while(t--)
{
prework();
solve();
}
return 0;
}