codeforces55D数位dp
查询给定区间内的beautiful number。 一个数字是beautiful number当且仅当能被自己的各个数字不为0的位整除。
这个dp的状态还是挺难想的。一个数能被自己的各个位整除,那么必须是这些位的最小公倍数的倍数。
那么可以想到的一个状态是dp[i][j][k] 为长度为i的时候,数字是组成的数字是j,各个位的最小公倍数是k, 那么当j%k==0,说明数字j是可行的。
当时j太大了,根本存不下。但是数字1-->9最大的一个最小公倍数是2520, 所以只要 j%2520%k==0就行了。
这是为什么呢 ,因为j可以分解为(k*2520 + x) % k , x=j%2520, 所以只要判断x%k是不是等于0就行了。
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <algorithm> 5 #include <iostream> 6 #include <queue> 7 #include <stack> 8 #include <vector> 9 #include <map> 10 #include <set> 11 #include <string> 12 #include <math.h> 13 using namespace std; 14 #pragma warning(disable:4996) 15 #pragma comment(linker, "/STACK:1024000000,1024000000") 16 typedef __int64 LL; 17 const int mod = 2520; 18 const int INF = 1<<30; 19 /* 20 dp[i][j][k] 21 表示前取了前i位,数字为j,各个位的最小公倍数位k 22 如果j%k==0 那么说明可以 23 24 dp[i+1][j*10 + i][lcm(k,i)] = dp[i][j][k] 25 26 */ 27 28 LL dp[30][2520][50]; 29 int num[64]; 30 bool vis[10000]; 31 int hs[2520 + 10]; 32 int gcd(int a, int b) 33 { 34 if (b == 0)return a; 35 return gcd(b, a%b); 36 } 37 int lcm(int a, int b) 38 { 39 return a*b / gcd(a, b); 40 } 41 void init() 42 { 43 int cnt = 0; 44 for (int i = 1; i <= mod; ++i) 45 if (mod%i == 0) 46 hs[i] = cnt++; 47 } 48 49 LL dfs(int pos, int preSum, int preLcm, bool flag) 50 { 51 if (pos == 0)return preSum % preLcm==0; 52 if (!flag && dp[pos][preSum][hs[preLcm]] != -1) 53 return dp[pos][preSum][hs[preLcm]]; 54 int limit = flag ? num[pos] : 9; 55 LL ans = 0; 56 for (int i = 0; i <= limit; ++i) 57 { 58 int nowSum = (preSum * 10 + i) % mod; 59 int nowLcm = preLcm; 60 if (i)nowLcm = lcm(nowLcm, i); 61 ans += dfs(pos - 1, nowSum, nowLcm, flag&&i == limit); 62 } 63 if (!flag)//如果pos为比n的第pos位小,那么说明可以利用已有的结果,如果不是的话,不能,因为可能比n大 64 dp[pos][preSum][hs[preLcm]] = ans; 65 return ans; 66 } 67 LL calc(LL n) 68 { 69 70 int len = 0; 71 while (n) 72 { 73 num[++len] = n % 10; 74 n /= 10; 75 } 76 77 return dfs(len,0,1,1); 78 } 79 int main() 80 { 81 init(); 82 int t; 83 LL l, r; 84 memset(dp, -1, sizeof(dp));//只初始化一次,因为计算的结果可多次利用 85 scanf("%d", &t); 86 while (t--) 87 { 88 scanf("%I64d%I64d", &l, &r); 89 printf("%I64d\n", calc(r) - calc(l-1)); 90 } 91 return 0; 92 }