题目链接:
https://codeforces.com/contest/55/problem/D
题目大意:
给你一个区间,让你求出这个区间里面有多少个数是满足这个数能被 他的所有位上的数 整除的。
具体思路:
对于能被他的所有位上的数整除这个条件,转换一下就是这个数能够整除他的所有位上的lcm。
但是这样的话还是需要求出这个数是谁。因为数的范围比较大,所以就应该考虑取模;
具体取模的时候,我们可以先算出1~9的lcm,这个是求的过程中的最大的lcm。我们在求这个数是什么的时候,就每次取模1~9的lcm就可以了。
但是这样的话,这个数组是要dp[19][2520+5][2520+5]这样的话,是会爆内存的。
然后内存还可以优化一下,这里的第三位表示的是当前的lcm 是多少,这一部分我们是可以预处理出来的,能够被2520整除的只有48个,也就说第三维我们是可以降到50的。
顺便还学到了一个数位DP的一个巨大的优化,就是对dp赋值的时候,我们没有必要每一次都对他清零。对于这个题,dp[i][j][k]表示前i位,当前的数是多大(mod2520),当前的位数lcm是多少。
也就是说这里并没有关于首位的标志(如果是开的四位的话,这样复杂度就会优化很小了)。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define ll long long 4 # define inf 0x3f3f3f3f 5 const int maxn = 2e5+100; 6 const int mod = 2520; 7 ll lcm(ll t1,ll t2) 8 { 9 return t1/__gcd(t1,t2)*t2; 10 } 11 int ord[mod+5]; 12 int tot; 13 init() 14 { 15 for(int i=1; i<=mod; i++) 16 { 17 if(mod%i==0) 18 { 19 ord[i]=++tot;// 映射 20 } 21 } 22 } 23 ll dp[20][2530][50]; 24 int a[maxn]; 25 ll dfs(int pos,int sum,int Lcm,int is_head) 26 { 27 if(!pos) 28 return sum%Lcm==0;// 这里有个疑问,假设当前的是 2 * 5 * 8. 如果已经存在 80 这个数,当i==2的时候, 下一次循环是sum==2,然后lcm==10,这样的话是是符合条件的情况,但是对这个代码却不符合 29 if(!is_head&&dp[pos][sum][ord[Lcm]]!=-1) 30 return dp[pos][sum][ord[Lcm]]; 31 int fmax = is_head ? a[pos] : 9; 32 ll ans=0; 33 for(int i=0; i<=fmax; i++) 34 { 35 ll tmp= ( i==0 ? Lcm : lcm(Lcm,i) ); 36 ans+=dfs(pos-1,(sum*10+i)%mod,tmp,is_head&&i==fmax); 37 } 38 if(!is_head) 39 dp[pos][sum][ord[Lcm]]=ans; 40 return ans; 41 } 42 ll solve(ll n) 43 { 44 // memset(a,0,sizeof(a)); 45 int num=0; 46 while(n) 47 { 48 a[++num]=n%10; 49 n/=10; 50 } 51 52 return dfs(num,0,1,1); 53 } 54 int main() 55 { 56 memset(dp,-1,sizeof(dp)); 57 init(); 58 int T; 59 scanf("%d",&T); 60 while(T--) 61 { 62 ll l,r; 63 scanf("%lld %lld",&l,&r); 64 printf("%lld ",solve(r)-solve(l-1)); 65 } 66 return 0; 67 }