数位DP复习小结

Posted LadyLex

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DP复习小结相关的知识,希望对你有一定的参考价值。

转载请注明原文地址http://www.cnblogs.com/LadyLex/p/8490222.html 

之前学数位dp的时候底子没打扎实

虚的要死

这次正好有时间……刷了刷之前没做的题目

感觉自己脑洞不太够……比较经典的题或者见过的类似模型就能自己推出来,但是没有见过的模型就虚的要死(比如二进制数位DP)

感谢WQ的帮助,让我对数位DP的理解逐渐加深

那么我们总结一下这次做的题目……

bzoj4521

  记忆化搜索即可,水爆

 1 #include <cstring>
 2 #include <cstdio>
 3 using namespace std;
 4 #define RG register
 5 #define LL long long
 6 int bin[15],cnt;
 7 LL poww[15],f[12][10][3][3][2][2];
 8 inline int min(int a,int b){return a<b?a:b;}
 9 inline int max(int a,int b){return a>b?a:b;}
10 inline LL dfs(int st,int pre,int comb,int maxcomb,bool have8,bool have4,bool limit)
11 {
12     if(st==0)return maxcomb==3;
13     if(!limit&&f[st][pre][comb][maxcomb][have8][have4]!=-1)
14         return f[st][pre][comb][maxcomb][have8][have4];
15     LL ret=0;
16     RG int i,tmp,lim=(limit)?bin[st]:10;
17     for(i=0;i<lim;++i)
18     {
19         tmp=min((pre==i)?comb+1:1,3);
20         if(i==4)
21         {
22             if(!have8)ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),0,1,0);
23         }
24         else if(i==8)
25         {
26             if(!have4)ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),1,0,0);
27         }
28         else ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),have8,have4,0);
29     }
30     if(limit)
31     {
32         tmp=min((pre==i)?comb+1:1,3);
33         if(i==4)
34         {
35             if(!have8)ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),0,1,1);
36         }
37         else if(i==8)
38         {
39             if(!have4)ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),1,0,1);
40         }
41         else ret+=dfs(st-1,i,tmp,max(tmp,maxcomb),have8,have4,1);
42     }
43     if(!limit)f[st][pre][comb][maxcomb][have8][have4]=ret;
44     return ret;
45 }
46 inline LL calc(LL len)
47 {
48     LL ret=0;cnt=0;
49     while(len)bin[++cnt]=len%10,len/=10;
50     for(RG int i=1;i<bin[11];++i)ret+=dfs(10,i,1,1,i==8,i==4,0);
51     return ret+dfs(10,bin[11],1,1,bin[11]==8,bin[11]==4,1);
52 }
53 int main()
54 {
55     LL l,r,ans;RG int i;
56     for(poww[0]=i=1;i<=12;++i)poww[i]=poww[i-1]*9;
57     scanf("%lld%lld",&l,&r);
58     memset(f,-1,sizeof(f)),ans=calc(r);
59     if(l>1e10)ans-=calc(l-1);
60     printf("%lld\\n",ans);
61 }
BZOJ4521

bzoj4029

  假装他是数位dp的小模拟

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define RG register
 5 #define LL long long
 6 int br[12],bl[12],lr,ll,_10[12];
 7 int main()
 8 {
 9     RG int pos,i,j,l,r,tmp,t,ans;
10     scanf("%d",&t);
11     for(_10[1]=1,i=2;i<=10;++i)_10[i]=_10[i-1]*10;
12     while(t--)
13     {
14         scanf("%d%d",&l,&r),--l;
15         ll=lr=0;
16         tmp=r;while(tmp)br[++lr]=tmp%10,tmp/=10;
17         tmp=l;while(tmp)bl[++ll]=tmp%10,tmp/=10;
18         if(lr!=ll)
19         {
20             if(l<5*_10[ll])ans=5*_10[ll];
21             else if(5*_10[ll+1]<=r)ans=5*_10[ll+1];
22             else if(bl[ll]==9)ans=_10[ll+1];
23             else ans=_10[ll]*(bl[ll]+1);
24         }
25         else
26         {
27             pos=ll;ans=0;
28             while(bl[pos]==br[pos]&&pos)ans=ans*10+bl[pos],--pos;
29             if(pos)
30             {
31                 if(bl[pos]<5&&5<=br[pos])ans=(ans*10+5)* _10[pos];
32                 else ans=(ans*10+bl[pos]+1)* _10[pos];
33             }
34         }
35         printf("%d\\n",ans);
36     }
37 }
BZOJ4029

bzoj3209

  从低位往高位DP,带个组合数乱搞,比较水,没有打

bzoj3329

  两边同时异或一下x,然后由于异或是不进位的加法,所以发现性质是没有相邻的1,dp即可

 1 #include <cstring>
 2 #include <cstdio>
 3 using namespace std;
 4 #define mod 1000000007
 5 #define RG register 
 6 #define LL long long
 7 namespace work1
 8 {
 9     LL f[64][2];int bin[64],len;
10     inline void init()
11     {
12         f[1][0]=1,f[1][1]=1;
13         for(RG int i=2;i<=62;++i)
14             f[i][0]=f[i-1][0]+f[i-1][1],f[i][1]=f[i-1][0];
15     }
16     inline LL dfs(int st,int pre,bool lim)
17     {
18         if(st==0)return 1;
19         if(!lim)return pre?f[st][0]:f[st][0]+f[st][1];
20         if(bin[st])
21             return pre?dfs(st-1,0,0):(dfs(st-1,0,0)+dfs(st-1,1,1));
22         return dfs(st-1,0,1);
23     }
24     inline LL work(LL n)
25     {
26         if(!n)return 0;
27         len=0;while(n)bin[++len]=n&1,n>>=1;
28         return dfs(len-1,0,0)+dfs(len-1,1,1)-1;
29     }
30 }
31 namespace work2
32 {
33     struct matrix
34     {
35         int a[4][4];
36         inline void clear(){memset(a,0,sizeof(a));}
37         inline void init(){memset(a,0,sizeof(a));for(RG int i=0;i<4;++i)a[i][i]=1;}
38         inline matrix operator * (const matrix &b)const
39         {
40             RG int i,j,k;
41             matrix c;c.clear();
42             for(i=0;i<4;++i)
43                 for(k=0;k<4;++k)if(a[i][k])
44                     for(j=0;j<4;++j)if(b.a[k][j])
45                         c.a[i][j]=(c.a[i][j]+(LL)a[i][k]*b.a[k][j])%mod;
46             return c;
47         }
48         inline void print()
49         {
50             for(RG int i=0;i<4;++i,printf("\\n"))
51                 for(RG int j=0;j<4;++j)
52                     printf("%d ",a[i][j]);
53         }
54     }d,t;
55     inline void init()
56     {
57         d.clear(),d.a[0][0]=d.a[0][1]=d.a[0][2]=d.a[1][0]=d.a[1][3]=1;
58     }
59     inline matrix quick_mod(matrix di,LL mi)
60     {
61         matrix ret;ret.init();
62         for(;mi;mi>>=1,di=di*di)if(mi&1)ret=ret*di;
63         return ret;
64     }
65     inline int work(LL n)
66     {
67         t.clear(),t.a[0][0]=t.a[0][1]=1,t=t*quick_mod(d,n);
68         return t.a[0][0];
69     }
70 }
71 int main()
72 {
73     RG int i,j,t;LL n;
74     scanf("%d",&t);
75     work1::init(),work2::init();
76     while(t--)
77         scanf("%lld",&n),printf("%lld\\n%d\\n",work1::work(n),work2::work(n));
78 }
BZOJ3329

bzoj1799

  打表发现数位之和很少,枚举数位之和,但是我没想到dp的定义……

  然后看了一下题解的数组定义,难度适中吧……没有那么难,但是自己还是没想出来

  那个维护当前值然后每次cur=cur×10+i的很巧妙,没想出来……

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define RG register
 6 #define LL long long
 7 int bin[20];
 8 inline int min(int a,int b){return a<b?a:b;}
 9 inline int max(int a,int b){return a>b?a:b;}
10 int mod,mark[20][163][163][2];
11 LL f[20][163][163][2];
12 inline LL dfs(int st,int sum,int cur,bool lim)
13 {    
14     if(st==0)return cur==0&&sum==0;
15     if(mark[st][sum][cur][lim]==mod)return f[st][sum][cur][lim];
16     mark[st][sum][cur][lim]=mod;LL ret=0;
17     RG int i,l=max(0,sum-(st-1)*9 ),r=min( sum+1,(lim?bin[st]:10) );
18     for(i=l;i<r;++i)ret+=dfs(st-1,sum-i,(cur*10+i)%mod,0);
19     if(lim && sum>=bin[st] )
20         ret+=dfs(st-1,sum-bin[st],(cur*10+bin[st])%mod,1);
21     return f[st][sum][cur][lim]=ret;
22 }
23 inline LL calc(LL n)
24 {
25     RG int cnt=0;LL ret=0;
26     while(n)bin[++cnt]=n%10,n/=10;
27     memset(mark,0,sizeof(mark));
28     for(mod=1;mod<=162;++mod)ret+=dfs(cnt,mod,0,1);
29     return ret;
30 }
31 int main()
32 {
33     RG int i,j;LL l,r;
34     scanf("%lld%lld",&l,&r);
35     printf("%lld\\n",calc(r)-calc(l-1));
36 }
BZOJ1799

bzoj1183+bzoj2713

  这个和之前那个淘金很像……先预处理乘积,后面那个我一开始一直在想像上面1799的搞法带着乘积走

  然后发现不行,最后得到了提示,对于每一个乘积x,我去查询l/x,r/x区间内乘积为x的数就行了

  这个转化和之前数学上那个gcd很像:d|ij <==> d/gcd(i,d) | j

  啊我好蠢啊

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <cstdlib>
 5 using namespace std;
 6 #define RG register
 7 #define LL long long
 8 #define L 440000
 9 #define UL 44000
10 int bin[20],len,cnt;
11 LL _9[20],mul[L],f[20][UL],l,r;
12 inline void dfs1(int st,int pre,LL multi)
13 {
14     mul[++cnt]=multi;
15     if(st==11)return;
16     for(RG int i=pre;i<10;++i)
17         if(multi*i<=r)dfs1(st+1,i,multi*i);
18 }
19 inline int id(LL val){return lower_bound(mul+1,mul+cnt+1,val)-mul;}
20 inline LL dfs(int st,int mul_id,bool limit)
21 {
22     if(mul[mul_id]>_9[st])return 0;
23     if(st==0)return 1;
24     RG int i,lim=(limit?bin[st]:10);
25     LL ret=0;
26     for(i=1;i<lim;++i)
27         if(mul[mul_id]%i==0)
28             ret+=f[st-1][id(mul[mul_id]/i)];
29     if(lim&&bin[st])
30         if(mul[mul_id]%bin[st]==0)
31             ret+=dfs(st-1,id(mul[mul_id]/bin[st]),1);
32     return ret;
33 }
34 inline LL query(int id,LL n)
35 {
36     LL ret=0;len=0;
37     while(n)bin[++len]=n%10,n/=10;
38     for(RG int i=1;i<len;++i)ret+=f[i][id];
39     return ret+dfs(len,id,1);
40 }
41 inline LL calc(LL n)
42 {
43     if(!n)return 0;LL ret=0;
44     for(RG int i=1;mul[i]<=n&&i<=cnt;++i)
45         ret+=query(i,n/mul[i]);
46     return ret;
47 }
48 int main()
49 {
50     RG int i,j,k;
51     scanf("%lld%lld",&l,&r);
52     mul[++cnt]=1;
53     dfs1(0,2,1),sort(mul+1,mul+cnt+1),
54     cnt=unique(mul+1,mul+cnt+1)-mul-1;
55     for(_9[0]=i=1;i<=18;++i)_9[i]=_9[i-1]*9;
56     for(f[0][1]=1,i=1;i<=9;++i)f[1][i]=1;
57     for(i=2;i<=18;++i)
58         for(j=1;j<=cnt;++j)if(f[i-1][j])
59             for(k=1;k<=9;++k)f[i][id(mul[j]*k)]+=f[i-1][j];
60     printf("%lld\\n", calc(r) - calc(l-1) );
61 }
BZOJ1183+BZOJ2713

bzoj3530

  不知道怎么混进来的AC自动机题目

  前导0的处理需要注意,一开始没有想好

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define L 1510
 5 #define RG register
 6 #define mod 1000000007
 7 char s[L],str[L];
 8 bool ban[L];
 9 int n,cnt,ch[L][11],fail[L],q[L],hd,tl,plan[L][L];
10 inline int dfs(int rt,int left)
11 {
12     if(ban[rt])return plan[rt][left]=0;
13     if(plan

以上是关于数位DP复习小结的主要内容,如果未能解决你的问题,请参考以下文章

数位DP小结

数位dp小结

数位dp小结以及模板

数位dp,贪心,线性dp复习总结

10.16复习 数位DP——不要62

数位DP数位小孩