codeforces 55D Beautiful numbers(数位dp)
题目:戳我
题意:找出区间[l, r] 的所有数的个数,满足数能被每一位数字整除。
思路:
1~9 的最小公倍数为2520,所以如果一个数满足条件,满满它被2520整除。1~2520内只被2520整除的大约有50个,设dp[20][2525][50](i, j, k)表示前 i 个数模2520 为j 且这i 个数的最小公倍数为 o[k],k表示1~2520内第o[k]个被2520整除的数。
dp[i+1][mod(j*10+d, 2520)][o[lcm(k, d)]] <= dp[i][j][o[k]]
(预处理可快10倍左右)
code:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 6 inline int gcd(int a, int b) {return b ? gcd(b, a%b) : a;} 7 8 ll f[20][255][25<<1]; 9 int dg[20]; 10 int k; 11 int o[2525], g[2525][10], mo[255][10], oo[25<<1]; 12 13 void pc() { 14 k = 0; 15 for(int i = 1; i < 2521; ++i) if(2520%i == 0) 16 o[i] = ++k, oo[k] = i; 17 for(int j = 0; j < 10; ++j){ 18 for(int i = 1, q = j ? j : 1; i <= k; ++i) 19 g[oo[i]][j] = oo[i]*q/gcd(oo[i], q) ; 20 for(int i = 0; i < 252; ++i) 21 mo[i][j] = (i*10+j) % 252; 22 } 23 return ; 24 } 25 26 ll dfs(int i, int s, int t, bool e) { 27 if(i == -1) return s % t == 0; 28 if(!e && ~f[i][s][o[t]]) return f[i][s][o[t]]; 29 int u = e ? dg[i] : 9; 30 ll res = 0; 31 for(int d = 0; d <= u; ++d) { 32 res += dfs(i-1, i ? mo[s][d] : (s*10+d), g[t][d], e&&d==u); 33 } 34 return e ? res : f[i][s][o[t]] = res; 35 } 36 37 ll solve(ll x) { 38 int len = 0; 39 for(; x; x /= 10) dg[len++] = x % 10; 40 return dfs(len-1, 0, 1, 1); 41 } 42 43 int main() 44 { 45 pc(); 46 int T; 47 scanf("%d", &T); 48 memset(f, -1, sizeof f); 49 ll l, r; 50 while(T--) { 51 scanf("%I64d %I64d", &l, &r); 52 printf("%I64d\n", solve(r) - solve(l-1)); 53 } 54 return 0; 55 }