数位dp中不错的题目
求能够整除自身各位数字的数,那换句话来说也就是能够整除各位数的最小公倍数,可以算出1-9所有数字的最小公倍数为2000+
从高位向下走的时候,要保留当前第几位i,当前lcm,以及前面对lcm的余数r,不过这个lcm是变换的,状态无法保存。
可以看下这个式子 x%m = x%(2*m)%m 显然,是可以的。那么就想到有没有一个lcm是所有可能出现的lcm的倍数,可以想到就是上面算出的2000+
现在就能确定函数里的几个参数了,i,lcm,MOD,r 但是这样存的话 明显数组开不了,又可以想下其实1-9中算出来的最小公倍数的数量不会有2000+这么多
1 2 3 4 5 6 7 8 9 最后为 5 7 8 9 也就是 5 7 2^3 3^2 那么总共的最小公倍数数量也就为2*2*4*3 = 48 这样就可以节省内存了。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 2520 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 LL dp[22][50][N+2]; 18 int d[20],p[55],o,po[N+2]; 19 void init() 20 { 21 int i,g=0; 22 23 for(i = 1; i <= N ; i++) 24 if(N%i==0) 25 po[i] = ++g; 26 27 } 28 LL dfs(int i,bool e,int lcm,int r) 29 { 30 if(i==-1) 31 return r%lcm==0; 32 if(!e&&dp[i][po[lcm]][r]!=-1) 33 return dp[i][po[lcm]][r]; 34 int j; 35 int mk = e?d[i]:9; 36 LL ans = 0; 37 for(j = 0 ;j <= mk ; j++) 38 { 39 if(j==0) 40 ans+=dfs(i-1,e&&j==mk,lcm,(r*10)%N); 41 else 42 { 43 int ll = lcm/__gcd(lcm,j)*j; 44 ans+=dfs(i-1,e&&j==mk,ll,(r*10+j)%N); 45 } 46 } 47 return e?ans:dp[i][po[lcm]][r] = ans; 48 } 49 LL cal(LL x) 50 { 51 int g = 0; 52 while(x) 53 { 54 d[g++] = x%10; 55 x/=10; 56 } 57 return dfs(g-1,1,1,0); 58 } 59 int main() 60 { 61 int t; 62 LL l,r; 63 cin>>t; 64 init(); 65 memset(dp,-1,sizeof(dp)); 66 while(t--) 67 { 68 cin>>l>>r; 69 cout<<cal(r)-cal(l-1)<<endl; 70 } 71 return 0; 72 }