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 }
View Code

 

posted @ 2016-03-07 17:14  妮king狼  阅读(299)  评论(0编辑  收藏  举报