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 }