D. Beautiful numbers(数位DP)

题目链接:

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;
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\n",solve(r)-solve(l-1));
65     }
66     return 0;
67 }

 

猜你喜欢

转载自www.cnblogs.com/letlifestop/p/11014847.html