数位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 }
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 }
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 }
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 }
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 }
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复习小结的主要内容,如果未能解决你的问题,请参考以下文章