BZOJ 4521 CQOI2016 手机号码 数位dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4521

 

题意概述:给出一个区间[L,R],统计区间中满足:1、4,8不同时出现;2、至少有3个相邻的相同数字 的数字个数。10^10<=L

 

实际上要想个状态方程和转移很好想,只是细节和答案就有点。。。。

简单来说,设f(i,x,y,0/1/2,0/1)表示第i位上的数是x,第i-1位上的数是y,4,8没有出现过 / 4出现过 / 8出现过,有/无三个相邻的相同数字的数字数量。

转移的时候f(1,x,y,0/1/2,0/1)可以直接暴力初始化,然后转移过程中对方程后两个位置上的状态讨论即可(分为之前就是这样和之前不是这样但是这一步变成了这样两种)。

计算答案的时候从高到低考虑,同时注意当前确定下来的数里面4,8出现的情况以及有无相邻的三个数字,以加上限制或者增加更新范围。

最后说一点建议L那个位置特判一下否则很惨。。。。

 

然后循环的时候上界不要写错了,不要脑子不知道发生了什么把10写成了9之类的耗掉了1h,不要问我怎么知道的!!!!!

 

 1 #include
 2 #include
 3 #include
 4 #include
 5 #include
 6 #include
 7 #include
 8 #include<set>
 9 #include
10 #include
11 #include
12 using namespace std;
13 typedef long long LL;
14 
15 LL L,R,f[13][10][10][3][2];
16 
17 void dp()
18 {
19     for(int x=0;x<10;x++)
20     for(int y=0;y<10;y++){
21         f[1][x][y][0][0]=x!=4&&x!=8&&y!=4&&y!=8?1:0;
22         f[1][x][y][1][0]=(x==4||y==4)&&x!=8&&y!=8?1:0;
23         f[1][x][y][2][0]=(x==8||y==8)&&x!=4&&y!=4?1:0;
24     }
25     for(int i=2;i<=10;i++)
26     for(int x=0;x<10;x++){
27         for(int y=0;y<10;y++)
28         for(int z=0;z<10;z++){
29             if(x!=4&&x!=8&&(x!=y||x!=z)) f[i][x][y][0][0]+=f[i-1][y][z][0][0];
30             if(x!=8&&(x!=y||x!=z)) f[i][x][y][1][0]+=f[i-1][y][z][1][0];
31             if(x==4&&(x!=y||x!=z)) f[i][x][y][1][0]+=f[i-1][y][z][0][0];
32             if(x!=4&&(x!=y||x!=z)) f[i][x][y][2][0]+=f[i-1][y][z][2][0];
33             if(x==8&&(x!=y||x!=z)) f[i][x][y][2][0]+=f[i-1][y][z][0][0];
34             if(x!=4&&x!=8) f[i][x][y][0][1]+=f[i-1][y][z][0][1];
35             if(x!=8) f[i][x][y][1][1]+=f[i-1][y][z][1][1];
36             if(x==4) f[i][x][y][1][1]+=f[i-1][y][z][0][1];
37             if(x!=4) f[i][x][y][2][1]+=f[i-1][y][z][2][1];
38             if(x==8) f[i][x][y][2][1]+=f[i-1][y][z][0][1];
39         }
40         if(x!=4&&x!=8) f[i][x][x][0][1]+=f[i-1][x][x][0][0];
41         if(x!=8) f[i][x][x][1][1]+=f[i-1][x][x][1][0];
42         if(x!=4) f[i][x][x][2][1]+=f[i-1][x][x][2][0];
43     }
44 }
45 LL calc(LL A)
46 {
47     int n[15]={0},cnt=0;
48     n[cnt++]=A%10,A/=10;
49     while(A) n[cnt++]=A%10,A/=10;
50     LL re=0;
51     for(int x=1;x10];x++)
52     for(int y=0;y<10;y++)
53         re+=f[10][x][y][0][1]+f[10][x][y][1][1]+f[10][x][y][2][1];
54     int x=n[10];
55     int ok=0,p4=x==4,p8=x==8;
56     for(int i=10;i>=1;i--){
57         for(int y=0;y1]+(i==1);y++){
58             if(y==4&&p8||y==8&&p4) continue;
59             re+=f[i][x][y][0][1];
60             if(!p8) re+=f[i][x][y][1][1];
61             if(!p4) re+=f[i][x][y][2][1];
62             if(ok||x==y&&x==n[i+1]){
63                 re+=f[i][x][y][0][0];
64                 if(!p8) re+=f[i][x][y][1][0];
65                 if(!p4) re+=f[i][x][y][2][0];
66             }
67         }
68         x=n[i-1],p4|=(x==4),p8|=(x==8);
69         if(n[i-1]==n[i]&&n[i]==n[i+1]) ok=1;
70         if(p4&&p8) break;
71     }
72     return re;
73 }
74 int check(LL x)
75 {
76     int n[15]={0},cnt=0,p4=0,p8=0,ok=0;
77     n[cnt++]=x%10,x/=10;
78     while(x) n[cnt++]=x%10,x/=10;
79     for(int i=0;i){
80         p4|=n[i]==4,p8|=n[i]==8;
81         if(p4&&p8) return 0;
82         if(i>1&&n[i]==n[i-1]&&n[i]==n[i-2]) ok=1;
83     }
84     return ok;
85 }
86 int main()
87 {
88     cin>>L>>R;
89     dp();
90     cout<'\n';
91     return 0;
92 }

 

转载于:https://www.cnblogs.com/KKKorange/p/8562653.html

你可能感兴趣的:(BZOJ 4521 CQOI2016 手机号码 数位dp)