CF 55D Beautiful Numbers - 数位DP

Description

求$[L, R]$内, 有多少个数能被自己所有位上的数整除

Solution

妥妥的数位DP, 但是就是不会做2333, 状态很难想到。

显然不可能把最后的数作为一个状态。

我们想到一个数如果整除所有位上的数, 肯定整除他们的最小公倍数, 所以把所有位上的数的最小公倍数作为一个状态。

然后我们发现$1~9$的最小公倍数只有$2520$, 所以只要$%上2520$作为一个状态, 到最后的时候仍然能够判断一个数是否被最小公倍数整除。

所以$sum[pos][lcm][mod]$ 为状态进行数位DP即可

Code

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<map>
 5 #define rd read()
 6 #define ll long long
 7 #define R register 
 8 using namespace std;
 9 
10 const int mod = 2520;
11 
12 int T, a[25];
13 ll n, m, sum[20][50][mod + 10];
14 int tot, b[300], vis[3000];
15 
16 ll read() {
17     R ll X = 0; R int p = 1; R char c = getchar();
18     for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1;
19     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
20     return X * p;
21 }
22 
23 int fd(int x) {
24     return lower_bound(b + 1, b + 1 + tot, x) - b;
25 }
26 
27 int gcd(int x, int y) {
28     return x % y ? gcd(y, x % y) : y;
29 }
30 
31 ll dfs(int pos, int lcm, int mo, bool lim) {
32     if(!pos) return mo % b[lcm] ? 0 : 1;
33     if(!lim && sum[pos][lcm][mo] != -1) return sum[pos][lcm][mo];
34     int up = lim ? a[pos] : 9;
35     ll tmp = 0, nxt;
36     for(int i = 0; i <= up; ++i) {
37         nxt = (b[lcm] * (i ? i : 1)) / gcd(b[lcm], i? i : 1);
38         tmp += dfs(pos - 1, fd(nxt), (mo * 10 + i) % mod, lim && a[pos] == i);
39     }
40     if(!lim) sum[pos][lcm][mo] = tmp;
41     return tmp;
42 }
43 
44 ll work(ll x) {
45     int len = 0;
46     while(x) {
47         a[++len] = x % 10;
48         x /= 10;
49     }
50     return dfs(len, 1, 0, true);
51 }
52 
53 void sec(int dep, int lcm) {
54     if(!vis[lcm]) b[++tot] = lcm, vis[lcm] = 1;
55     if(dep == 10) return;
56     sec(dep + 1, lcm);
57     sec(dep + 1, (lcm * dep) / gcd(lcm, dep));
58 }
59 
60 void init() {
61     sec(1, 1);
62     sort(b + 1, b + 1 + tot);
63     memset(sum, -1, sizeof(sum));
64 }
65 
66 int main()
67 {
68     T = rd;
69     init();
70     for(; T; T--) {
71         ll l = rd, r = rd;
72         printf("%lld\n", work(r) - work(l - 1));
73     }
74 }
View Code

猜你喜欢

转载自www.cnblogs.com/cychester/p/9630423.html