数位DP复习小结

之前学数位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[rt][left])return plan[rt][left];
14     int ret=0;
15     for(RG int i=0;i<10;++i)
16         if(!ban[ch[rt][i]])
17             ret=(ret+dfs(ch[rt][i],left-1))%mod;
18     return plan[rt][left]=ret;
19 }
20 #define id(c) ((c)-‘0‘)
21 signed main()
22 {
23     RG int i,j,m,rt,l,d,ans=0,good=1;
24     scanf("%s",s+1),n=strlen(s+1);
25     scanf("%d",&m);
26     cnt=1;
27     for(i=1;i<=m;++i)
28     {
29         scanf("%s",str+1),l=strlen(str+1);
30         for(rt=1,j=1;j<=l;++j)
31         {
32             d=str[j]-‘0‘;
33             if(!ch[rt][d])ch[rt][d]=++cnt;
34             rt=ch[rt][d];
35             if(ban[rt])break;
36         }
37         ban[rt]=1;
38     }
39     for(hd=1,tl=0,i=0;i<10;++i)
40         if(ch[1][i])q[++tl]=ch[1][i],fail[ch[1][i]]=1;
41         else ch[1][i]=1;
42     while(hd<=tl)
43         for(rt=q[hd++],i=0;i<10;++i)
44             if(ch[rt][i])d=ch[rt][i],fail[d]=ch[fail[rt]][i],ban[d]|=ban[fail[d]],q[++tl]=d;
45             else ch[rt][i]=ch[fail[rt]][i];
46     for(i=1;i<=cnt;++i)if(!ban[i])plan[i][0]=1;
47     for(i=1;i<=cnt;++i)if(!ban[i])
48         for(j=1;j<n;++j)if(!plan[i][j])dfs(i,j);
49     for(i=1;i<n;++i)for(j=1;j<10;++j)
50         ans=(ans+plan[ch[1][j]][i-1])%mod;
51     for(rt=1,i=1;i<=n;++i)
52     {
53         for(d=s[i]-‘0‘,j=(i==1?1:0);j<d;++j)
54             if(!ban[ch[rt][j]])ans=(ans+plan[ch[rt][j]][n-i])%mod;
55         rt=ch[rt][d];
56         if(ban[rt]){good=0;break;}
57     }
58     printf("%d\n",ans+good);
59 }

BZOJ3530

uoj86

题解在这里面,综合感不错的题目……

bzoj3652

相当于两部分分开求和再除总数然后加权

第二部分求异或和很好想,统计每位0/1个数然后搞就行

关于第一部分………在%wq的思路之后打过去了

第一部分相当于对于每一个数选一个数和它异或和最大

由于选的那个数也要在n的范围内,所以我们不太好搞

这样,我们正难则反,先认为每个数的每一位都能贡献上,再减去多余的贡献

由于n的二进制第一位一定是1,所以在2^(len-1)到n-1这些数一定每一位都能贡献上

那么我们一开始就拿着0~2^(len-1)-1这些数(len代表n的二进制长度),维护剩下多少数

从n的高位往低位考虑,如果n的某一位是1,那么这一位如果填0的话,

现在这一位为1的数(正好占一半)就一定能贡献后面所有位的值(即他们一定不会超限,所以后面可以随意填)

那么我们把它们扔掉,即更改剩下数的多少

这个和一开始分析的最高位的情况类似

反之,如果为0,那么这一位为0的所有数(也是占一半)由于不能填1而无法贡献这一位的异或值

那么我们在答案中减去对应的值

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 #define RG register
 6 #define LL long long
 7 #define db double
 8 LL bin[64];
 9 int bit[64],len;
10 int main()
11 {
12     RG int i;LL n,m,tmp,cnt0,cnt1;
13     db val1=0,val2=0,p,q;
14     for(bin[0]=i=1;i<=62;++i)bin[i]=bin[i-1]<<1;
15     scanf("%lld%lf",&n,&p),m=n-1;
16     tmp=m;while(tmp)bit[++len]=tmp&1,tmp>>=1;
17     for(i=len;~i;--i)
18     {
19         tmp=n>>(i+1),cnt0=cnt1=tmp*bin[i];
20         if(n&bin[i])cnt0+=bin[i],cnt1+=(bin[i]-1)&n;
21         else cnt0+=(bin[i]-1)&n;
22         val2+=2.0*cnt0*cnt1*bin[i];
23     }
24     val2/=n*1.0*n;
25     val1=n*1.0*(bin[len]-1);tmp=bin[len];
26     for(i=len;i;--i)
27         if(bit[i])tmp>>=1;
28         else val1-=(tmp>>1)*1.0*bin[i-1];
29     printf("%.10lf\n",p*val1/n+(1-p)*val2 );
30 }

BZOJ3652

bzoj4513

感觉是上面一题的加强版

自己想的是dfs打法,但是由于情况太多无法手动分类讨论,打不出来

数组定义定义f[i][2][2][2],后面三个0/1分别代表是否满足i<=n,j<=m,i^j>=k

现在我发现的AC写法(茴香豆的茴有四种写法你知道嘛)

1.(大部分网上题解使用的)4维数组dp

发现网上大部分人是用的迭代dp的打法

和我定义是一样的,可是……

代码实现出人意料的简洁

dp写法和dfs写法的确是各有所长

dp适合用很多for循环和边界特判无脑处理掉大量的分类讨论

而dfs适合无法很好用for循环枚举的东西

Ps:dp写法有一个地方不是太懂,就是为什么最后要把数组的每个元素都计算一遍

我们需要的答案难道不就是ans[1][1][1]吗

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 #define RG register
 6 #define LL long long
 7 LL n,m,k,bin[64],f_ge[64][2][2][2],f_sum[64][2][2][2];
 8 int main()
 9 {
10     RG int mod,i,ta,tb,tc,a,b,c,x,y,z,t,len,lena,lenb,lenc,bita,bitb,bitc;
11     LL ans,tmp;scanf("%d",&t);
12     while(t--)
13     {
14         scanf("%lld%lld%lld%d",&n,&m,&k,&mod),--n,--m;
15         lena=lenb=lenc=0;
16         tmp=n;while(tmp)++lena,tmp>>=1;
17         tmp=m;while(tmp)++lenb,tmp>>=1;
18         tmp=k;while(tmp)++lenc,tmp>>=1;
19         len=max(lena,max(lenb,lenc));
20         for(bin[0]=i=1;i<=len;++i)bin[i]=(bin[i-1]<<1)%mod;
21         memset(f_ge,0,sizeof(f_ge)),memset(f_sum,0,sizeof(f_sum));
22         f_ge[len+1][1][1][1]=1;ans=0;
23         for(i=len;~i;--i)
24         {
25             bita=(n>>i)&1,bitb=(m>>i)&1,bitc=(k>>i)&1;
26             for(a=0;a<2;++a)for(b=0;b<2;++b)for(c=0;c<2;++c)
27                 if(f_ge[i+1][a][b][c])
28                     for(x=0;x<2;++x)
29                     {
30                         if(a && x>bita)break;
31                         for(y=0;y<2;++y)
32                         {
33                             if(b && y>bitb)break;
34                             z=x^y;
35                             if(c && z<bitc)continue;
36                             ta=(a && bita==x)?1:0,tb=(b && bitb==y)?1:0,tc=(c && bitc==z)?1:0;
37                             f_ge[i][ta][tb][tc]=(f_ge[i][ta][tb][tc]+f_ge[i+1][a][b][c])%mod;
38                             f_sum[i][ta][tb][tc]=( f_sum[i][ta][tb][tc]+f_sum[i+1][a][b][c])%mod;
39                             if(z)f_sum[i][ta][tb][tc]=( f_sum[i][ta][tb][tc]+ bin[i]*f_ge[i+1][a][b][c]%mod )%mod;
40                         }
41                     }
42         }
43         k%=mod;
44         for(a=0;a<2;++a)for(b=0;b<2;++b)for(c=0;c<2;++c)
45             ans=(ans+f_sum[0][a][b][c]-k*f_ge[0][a][b][c]%mod+mod)%mod;
46         printf("%lld\n",ans);
47     }
48 }

BZOJ4513

啊……我要再研究一下dfs写法,看看能不能打出来

upd:我完蛋了,我打不出来2333

2.wq式dp打法,强大又快速,wq Orzzzzzzzzzzzzzzzzzzzzzzzz

很巧妙,刷新了我对数位Dp的认识,让我理解更加深入了

让wq讲了一个多小时,感觉自己智商掉没了2333

但是还是不太懂,无法用语言描述……

等我更强了再来补这个做法……

bzoj3326

思路大概是很清真的,总的来说就是预处理前i位数的子串和,以及前缀子串之和,然后按照数位DP那样限制转移

但是……那个转移的式子……

啊真是让人痛苦,那个式子我总共大改了5遍才打过,并且有很多一开始没有注意到的细节

以后做题必须要先想好式子

每一项是什么为什么都要想清楚

要不特别耽误时间……有的时候船到桥头……就翻船了……

感觉思维严谨性得到了提高2333

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define RG register
 5 #define mod 20130427
 6 #define N 100010
 7 #define LL long long
 8 char cB[1<<15],*S=cB,*T=cB;
 9 #define getc (S==T&&(T=(S=cB)+fread(cB,1,1<<15,stdin),S==T)?0:*S++)
10 inline int read()
11 {
12     RG int x=0;RG char c=getc;
13     while(c<‘0‘|c>‘9‘)c=getc;
14     while(c>=‘0‘&c<=‘9‘)x=10*x+(c^48),c=getc;
15     return x;
16 }
17 inline int Sum(int a){return ((LL)a*(a+1ll)/2)%mod;}
18 inline int max(int a,int b){return a>b?a:b;}
19 inline int min(int a,int b){return a<b?a:b;}
20 int ge2[N],sum2[N],ge3[N],sum3[N],no_zero_sum3[N],B,lena,bit[N],lenb,bin[N];
21 inline int calc(bool start,int left,int lim,int cnt,int sum,int sum_of_substring )
22 {
23     if(lim==0)return 0;
24     if(start)
25         return
26             (
27             (LL) Sum(lim-1) * ge2[left] %mod * bin[left] %mod + //前与后连接之后前面的贡献
28             (LL) ( lim - 1 ) %mod * sum3[left] %mod + no_zero_sum3[left] %mod +//后面的子串
29             (LL) ( lim - 1 ) %mod * sum2[left] %mod//前与后连接之后后面的贡献
30             )%mod;
31     int new_sum=( (LL) sum * B %mod * lim %mod + (LL) Sum(lim-1) * cnt %mod ) %mod;
32     return
33         (
34         (LL) sum_of_substring * lim %mod * bin[left] %mod + //之前的子串
35         (LL) new_sum * ge2[left] %mod * bin[left] %mod + //前与后连接之后前面的贡献
36         (LL) lim %mod * sum3[left] %mod +//后面的子串 不乘cnt
37         (LL) cnt * lim %mod * sum2[left] %mod//前与后连接之后后面的贡献 ,要乘cnt 因为再前面不同
38         )%mod;
39 }
40 inline int dfs(bool start,int st,int cnt,int sum,int sum_of_substring)
41 {
42     if(st==0)return 0;
43     int new_sum=( (LL) sum * B %mod + (LL) ( cnt + 1 ) * bit[st] %mod )%mod;
44     return
45         ( new_sum + calc(start, st-1 , bit[st] , cnt + 1 , sum , sum_of_substring ) +
46         dfs(0, st - 1 , cnt + 1,  new_sum , (sum_of_substring + new_sum)%mod ) )%mod;
47 }
48 signed main()
49 {
50     RG int i,j,len,ans;
51     B=read(),lena=read();
52     for(i=lena;i;--i)bit[i]=read();
53     lenb=read();len=max(lena,lenb);
54     if(lena>1||bit[1]>0)
55     {
56         --bit[1],j=1;
57         while(bit[j]<0)bit[j]+=B,--bit[j+1],++j;
58         while(lena>1&&!bit[lena])--lena;
59     }
60     for(bin[0]=ge2[0]=i=1;i<=len;++i)
61     {
62         bin[i]=(LL)bin[i-1]*B%mod;
63         ge2[i]=( ge2[i-1] + bin[i] )%mod;
64         ge3[i]=(LL) Sum(i) * bin[i] %mod;
65         sum2[i]=( (LL) sum2[i-1] * B %mod +  Sum ( bin[i] - 1 ) )%mod;
66         sum3[i]=( (LL) Sum(B-1) * bin[i-1] %mod * ge2[i-1] %mod + (LL)B * sum2[i-1] %mod + (LL) B * sum3[i-1] %mod )%mod;
67         no_zero_sum3[i]=( (LL) Sum(B-1) * bin[i-1] %mod * ge2[i-1] %mod +
68                         (LL) (B - 1) * sum2[i-1] %mod + (LL) (B - 1) * sum3[i-1] %mod + no_zero_sum3[i-1] )%mod;
69     }
70     ans=mod-dfs(1,lena,0,0,0);
71     for(i=lenb;i;--i)bit[i]=read();
72     printf("%d\n",(ans+dfs(1,lenb,0,0,0))%mod);
73 }

bzoj3326

其实还有另外一种思路,那就是反过来从前往后走,并且用矩阵乘优化

这个是wq的原创思路……

bzoj3598

  没做呢,听说挺简单,但是我没想出来

  这次时间不够了……不想怂题解

  有时间再做……

这次做的题就是这么多……接下来要去找“有思维难度的DP题”了……加油咯……

原文地址:https://www.cnblogs.com/LadyLex/p/8490222.html

时间: 2024-10-11 01:36:34

数位DP复习小结的相关文章

数位DP小小结

FZOJ Problem 2113Jason的特殊爱好 题意:x~y数字里面有多少个 1 思路:我们算法课实验题的简化版,当时我用了很麻烦的一个DP=_= 刚刚学到了很棒的姿势,记忆化DP!! dfs(int pos ,bool end1) ; end1==false 返回pos位后面(包含pos)任意组合有多少个 1 : end1==true 返回上一位是结尾,Pos以后的位受到限制组合有多少个 1 : 大概是这样,如果数字是 4987 现在计算到 8 这个数字,end1==true,说明是4

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

通过一个月的奋(hua)斗(shui),我顺利的躺在了苏俄战场上. 这个月主要是 你的目标 数位dp 玄学 贪心 诡异 线性dp 提刀 ... 数位dp主要是有两种方法 一种是先把表刷完,再乱搞: 还有一种直接乱搞. 总而言之 都是乱搞过得. 贪心贪的巨诡异,感觉做了一些好题,贪心方法可能略微拓展了一下,贪心思想进一步加深. 总之,多玩贪吃蛇......... 线性dp,水题一大片,打消我的做题欲望,只要有什么地方写卡了就在那个地方加一维..... 没错就是这样,因为出题人太水了喜欢这样出..因

数位dp小结以及模板

这里是网址 别人的高一啊QAQ.... 嗯一般记忆化搜索是比递推好写的所以我写的都是dfs嗯......(因为我找不到规律啊摔,还是太菜.....) 显然这个东西的条件是非常的有套路..但是不管怎么样我就抄这个大佬的模板好了.... 找到合适的递推式是很重要的(都是废话) 一般数位dp都是和整除和各位数组成有关的.... 没了.....

数位DP入门:bzoj1833: [ZJOI2010]count 数字计数

膜拜了一下蔡大神....然后突然想起来一些东西然后就填了一个半年多前的坑= = 人生第一道自己写的数位DP...好吧以前是看题解然后也不知道为什么就过了的>_< 数位DP介绍: http://wenku.baidu.com/link?url=9OS5Ybpw5wx00ahrH8ED2oyIlR1uWwrxT8N4pEg27GgBt2T2hLe4sd_h1rmpY7P0HmeHIEDw9h6_K98dPhhjoMhD2TpKcS8w1X8cC_dkPp_ 接下来是题目地址: http://www

[poj3252]Round Numbers_数位dp

Round Numbers poj3252 题目大意:求一段区间内Round Numbers的个数. 注释:如果一个数的二进制表示中0的个数不少于1的个数,我们就说这个数是Round Number.给定区间l,r<=$2\cdot 10^9$. 想法:又是一道数位dp裸题.我们先来设状态:dp[i]表示二进制表示下有i为而且第一位是1的Round Number的个数. 这题的特殊之处在于我们并不需要转移?因为我们可以直接求出任意的dp[i].显然,我们的i位数的第一位是1,所以,后面0的个数一定

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>