题目链接:
https://codeforces.com/problemset/problem/55/D
题意:
找到区间范围中的数 \(x\) 的数量,要求 \(x\),能被它每一个非零的数位整除。
思路:
如果一个数能被它所有非零位数整除,那么它一定能被所有非零位数的最小公倍数整除,所以记忆化搜索的过程中要记录第 \(i\) 位之前的所有非零位数的最小公倍数。
接着看余数,因为 1 ~ 9 的最小公倍数为 2520,所以可以将数先对 2520 进行一个取余。这样子就可以定义 \(dp\) 数组 \(dp[i][j][k]\) 表示长度为 \(i\) 的数字,第 \(i\) 位之前所有的数字的最小公倍数为 \(j\),对 2520 取余后余数为 \(k\) 的数字有多少。数组大小为 20 * 2520 * 2520,超内存了。
因为 1 ~ 9 的数字的最小公倍数只有 48 种,所以可以通过离散化的方式让数组大小变成 20 * 50 * 2520。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL dp[20][50][2520], num[20], Hash[2520];
LL dfs(int pos, int limit, int prelcm, int res){
if (pos == 0) return res % prelcm == 0;
if (!limit && dp[pos][Hash[prelcm]][res] != -1) return dp[pos][Hash[prelcm]][res];
LL ans = 0;
int up = (limit ? num[pos] : 9);
for (int i = 0; i <= up; i ++ ){
int t = prelcm;
if (i) t = lcm(prelcm, i);
ans += dfs(pos - 1, limit && (i == num[pos]), t, (res * 10 + i) % 2520);
}
if (!limit) dp[pos][Hash[prelcm]][res] = ans;
return ans;
}
LL solve(LL x){
int len = 0;
while(x){
num[++ len] = x % 10;
x /= 10;
}
return dfs(len, 1, 1, 0);
}
void solve(){
memset(dp, -1, sizeof dp);
LL a, b;
cin >> a >> b;
cout << solve(b) - solve(a - 1) << "\n";
}
void init(){
int cnt = 0;
for (int i = 1; i <= 2520; i ++ )
if (2520 % i == 0)
Hash[i] = ++ cnt;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
init();
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}