几道数位DP

  因为这几天写的几道数位DP大多都太水。。而且也确实没什么好讲所以就扔到一起了。

 

[hdu4772]Good Numbers

  要求统计区间内 各位数之和能被10整除 的数的个数。

  练手,f[i][j][k]表示i位的数,以j开头,各位数之和取模10的结果为k,的方案数。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 int i,j,j1,k,k1,n,m;
 7 ll f[19][10][10],ten[19],l,r;
 8 
 9 int s[20],len;
10 inline ll get(ll x){
11     if(x<0)return 0;
12     int i,j,k,pre;ll tmp,ans=0;
13     for(i=0;i<=18;i++)if(x==ten[i]){x--;break;}
14     if(!x)return 1;
15     for(tmp=x,len=0;tmp;tmp/=10)s[++len]=tmp%10;
16     
17     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j][0];
18     for(i=1;i<s[len];i++)ans+=f[len][i][0];
19     
20     for(pre=s[len],i=len-1;i;i--){
21         k=(-pre+10)%10;
22         for(j=0;j<s[i];j++)ans+=f[i][j][k];
23         pre=(pre+s[i])%10;
24     }
25     return ans+(pre==0)+1;
26 }
27 int main(){
28     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
29     for(i=0;i<=9;i++)f[1][i][i]=1;
30     for(i=2;i<=18;i++)
31         for(j=0;j<=9;j++)for(k=0;k<=9;k++)
32         for(j1=0,k1=(k-j+10)%10;j1<=9;j1++)f[i][j][k]+=f[i-1][j1][k1];
33     
34     for(scanf("%d",&m),n=1;n<=m;n++){
35         scanf("%lld%lld",&l,&r);
36         printf("Case #%d: %lld\n",n,get(r)-get(l-1));
37     }
38     return 0;
39 }
View Code

 

[hdu2089]不要62

  要求统计区间内有多少个数 不包含62并且不包括4.

  f[i][j]表示i位的数,以j开头,非法数字的个数。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int f[10][10],ten[10],sum;
 6 int i,j,k,n,m;
 7 int now[12];
 8 inline int get(int x){
 9     if(!x)return 0;
10     int i,len=0,ans=0;
11     for(int tmp=x;tmp;tmp/=10)now[++len]=tmp%10;
12     now[len+1]=0;
13     for(i=len;i;i--){
14         for(j=0;j<now[i];j++){
15             ans+=f[i][j];//,printf("add:  %d %d\n",i,j);
16             if(j==2&&now[i+1]==6)ans+=ten[i-1]-f[i][j];
17         }
18         if(now[i]==4||(now[i]==2&&now[i+1]==6)){ans+=x%ten[i-1]+1;break;}
19     }
20 //    printf("%d  ans:%d\n",x,ans);
21     return ans;
22 }
23 int main(){    
24     for(i=0;i<=9;i++)f[1][i]=i==4;
25     ten[0]=1,ten[1]=10;
26     for(i=2;i<=9;i++){
27         ten[i]=ten[i-1]*10;
28         for(j=sum=0;j<=9;j++)sum+=f[i-1][j];
29         for(j=0;j<=9;/*printf("%d %d  %d\n",i,j,f[i][j]),*/j++)
30             if(j==4)f[i][j]=ten[i-1];
31             else if(j==6)f[i][j]=sum+ten[i-2]-f[i-1][2];
32             else f[i][j]=sum;
33     } 
34     while(scanf("%d",&n)==1){
35         scanf("%d",&m);
36         if(!n&&!m)break;
37         printf("%d\n",m-n+1-(get(m)-get(n-1)));
38     }
39     return 0;
40 }
View Code

 

[hdu3555]bomb

  要求统计区间内有多少个包含49的数。

  f[i][j]表示i位的数,j开头,包含49的数的个数。(因为49这个数比较特殊一点(9是最大的数)...统计的时候可以不写关于第二位数的特判)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 ll f[20][10],ten[23];
 7 int i,j,len,s[23];
 8 ll tmp,n;
 9 inline ll get(ll x){
10     ll ans=0;
11     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;s[len+1]=0;
12     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j];
13     for(i=len;i;i--){
14         for(j=i==len;j<s[i];j++)ans+=f[i][j];
15         if(s[i]==9&&s[i+1]==4){ans+=x%ten[i-1]+1;break;}
16     }
17     return ans;
18 }
19 int main(){
20     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*(ll)10;
21     for(i=1;i<=19;i++)
22         for(j=0;j<=9;/*printf("%d %d  %lld\n",i,j,f[i][j]),*/j++)
23         if(j!=4&&i>1)f[i][j]=f[i-1][j]*9+f[i-1][4];
24         else f[i][j]=f[i-1][1]*8+f[i-1][4]+ten[i-2];
25     int T;
26     for(scanf("%d",&T);T;T--)
27     scanf("%lld",&n),printf("%lld\n",get(n));
28     return 0;
29 }
View Code

 

[hdu4507]恨7不成妻

  预处理的时候,数字的平方和可以通过 方案数、数字和、数字的平方和 三个玩意算出来。。算是常见姿势了吧。

  再具体点见代码注释

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const int modd=1000000007;
 7 struct zs{
 8     ll num,sum,sqr;//方案数,方案的数字和,平方和
 9 }f[21][10][2][7][7];//f[i][x][j][k][l]:i位的数,开头数字为x,j表示有无某一位为7,k表示每一位加起来的和%7的值,l表示数字%7的值。
10 ll ten[23];
11 ll l,r;
12 
13 
14 inline void add(zs &a,zs b,ll num){
15     num%=modd;
16     a.num+=b.num,a.sum+=(b.sum+num*b.num)%modd,
17     a.sqr+=b.sqr+(b.sum*num%modd*2)+(b.num*num%modd*num%modd),
18     a.num-=a.num>=modd?modd:0,a.sum-=a.sum>=modd?modd:0,
19     a.sqr%=modd;
20 }
21 
22 int s[20],len;
23 inline ll get(ll num){
24     if(num==1)return 0;
25     register int i,j,k,x,pre2;ll tmp,ans=0,pre,pre1;zs b;
26     
27     for(tmp=num,len=0;tmp;tmp/=10)s[++len]=tmp%10;
28     for(i=1;i<len;i++)for(x=1;x<=9;x++)for(j=1;j<7;j++)for(k=1;k<7;k++)ans+=f[i][x][0][j][k].sqr,ans-=ans>=modd?modd:0;
29     for(x=1;x<s[len];x++)for(j=1;j<7;j++)for(k=1;k<7;k++)ans+=f[len][x][0][j][k].sqr,ans-=ans>=modd?modd:0;
30     
31     pre=s[len]*ten[len-1]%modd,pre1=s[len]*ten[len-1]%7,pre2=s[len]%7;
32     if(s[len]!=7)
33     for(i=len-1;i;i--){
34     //    printf("   i:%d   pre1:%lld   pre2:%d      pre:%lld\n",i,pre1,pre2,pre);
35         for(x=0;x<s[i];x++)for(j=1;j<7;j++)for(k=1;k<7;k++)
36             b=f[i][x][0][(j-pre2+7)%7][(k-pre1+7)%7],
37             ans=(ans+b.sqr+b.sum*pre%modd*2+b.num*pre%modd*pre%modd)%modd;
38         pre=(pre+s[i]*ten[i-1])%modd;pre1=(pre1+s[i]*ten[i-1])%7;pre2=(pre2+s[i])%7;
39         if(s[i]==7)break;
40     }
41     return ans;
42 }
43 int main(){
44     register int i,j,k,j1,k1,x;ll num;
45     
46     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
47     for(i=0;i<=9;i++)k=i%7,f[1][i][i==7][k][k]=(zs){1,i,i*i};
48     for(i=2;i<=19;i++)
49         for(j=0;j<7;j++)for(k=0;k<7;k++)
50         for(x=num=0;x<=9;x++,num+=ten[i-1]){
51             j1=j-x%7,j1+=j1<0?7:0,k1=k-(num%7),k1+=k1<0?7:0;
52             if(x!=7)
53                 for(int x1=0;x1<=9;x1++)add(f[i][x][0][j][k],f[i-1][x1][0][j1][k1],num);
54             else 
55                 for(int x1=0;x1<=9;x1++)add(f[i][x][1][j][k],f[i-1][x1][0][j1][k1],num);
56             for(int x1=0;x1<=9;x1++)add(f[i][x][1][j][k],f[i-1][x1][1][j1][k1],num);
57         }
58        
59     int T;
60     for(scanf("%d",&T);T;T--){
61         scanf("%lld%lld",&l,&r);
62         printf("%lld\n",(get(r+1)-get(l)+modd)%modd);
63     }
64     return 0;
65 }
View Code

 

[hdu3652]B-number

  要求出区间内包含13,并且能被13整除的数的个数。

  f[i][j][k][l]表示i位数,开头为j,k表示是否包含13,l表示数字模13后的结果,的方案数

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 int ten[11];
 7 int f[11][10][2][13];
 8 int i,j,k,j1,k1,n,m;
 9 
10 int s[12],len;
11 inline int get(int x){
12     if(x<13)return 0;
13     register int i,j,k,j1,k1,ans=0,pre;int tmp;
14     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;
15     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j][1][0];
16     for(i=1;i<s[len];i++)ans+=f[len][i][1][0];
17     
18     pre=(s[len]*ten[len-1])%13;bool flag=0;
19     for(i=len-1;i;i--){
20         k=(-pre+13)%13;
21         if(!flag)
22             for(j=0;j<s[i];j++)ans+=f[i][j][1][k];
23         else
24             for(j=0;j<s[i];j++)ans+=f[i][j][1][k]+f[i][j][0][k];
25         if(!flag&&s[i+1]==1&&s[i]>3)ans+=f[i][3][0][k];
26         if(s[i]==3&&s[i+1]==1)flag=1;
27         pre=(pre+s[i]*ten[i-1])%13;
28     }
29     if(flag)ans+=pre==0;
30     return ans;
31 }
32 int main(){
33     for(i=ten[0]=1;i<=10;i++)ten[i]=ten[i-1]*10;
34     for(i=0;i<=9;i++)f[1][i][0][i]=1;
35     for(i=2;i<=10;i++)for(j=0;j<=9;j++){
36         
37         for(k=0;k<13;k++){
38         for(j1=0,k1=(k-j*ten[i-1]%13+13)%13;j1<=9;j1++){
39             if(j!=1||j1!=3) f[i][j][0][k]+=f[i-1][j1][0][k1];
40             else f[i][j][1][k]+=f[i-1][j1][0][k1];
41             f[i][j][1][k]+=f[i-1][j1][1][k1];
42         }
43     //    printf("   %d %d %d %d\n",i,j,k,f[i][j][1][k]);
44         }
45     }
46     while(scanf("%d",&n)==1)
47         printf("%d\n",get(n));
48     
49     return 0;
50 }
View Code

 

[hdu3709]Balanced Number

  无力去膜题解。http://blog.csdn.net/acm_cxlove/article/details/7821726

  顺便感受了一下记忆化搜索在处理边界等方面的优越性= =

  感觉for的话就是负数不太好处理。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 int i,j,k,n,m;
 7 int s[19],len;ll tmp,l,r;
 8 ll f[19][19][1600],ten[19];
 9 bool u[19][19][1600];
10 
11 inline ll dfs(int x,int pos,int pre,bool limit){//考虑剩下的x位数字,支点已确定在pos
12     if(!x||pre<0) return pre==0;
13     if(!limit&&u[x][pos][pre])return f[x][pos][pre];
14     
15     int mx=limit?s[x]:9;ll ans=0;
16     for(int i=0;i<=mx;i++)
17         ans+=dfs(x-1,pos,pre-i*(pos-x),limit&&(i==mx));
18     if(!u[x][pos][pre]&&!limit)
19         f[x][pos][pre]=ans,u[x][pos][pre]=1;
20     return ans;
21 }
22 inline ll get(ll x){
23     ll ans=0;
24     for(int i=0;i<=18;i++)
25     if(x==ten[i]){x--,ans++;break;}
26     if(x<0)return 0;
27     
28     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;
29     for(int i=len;i;i--)
30         ans+=dfs(len,i,0,1);
31     return ans-len+1;
32 }
33 int main(){
34     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
35     int T;
36     for(scanf("%d",&T);T;T--){
37         scanf("%lld%lld",&l,&r);
38         printf("%lld\n",get(r)-get(l-1));
39     }
40     return 0;
41 }
View Code

 

UPD:其实以上写法中,f 数组的“以数字j开头”的那维都是不必要的。。。(所以我个傻逼预处理比别人多了两重循环TAT

posted @ 2016-02-25 22:02  czllgzmzl  阅读(179)  评论(0编辑  收藏  举报