BZOJ 4521 CQOI2016 手机号码 数位dp
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4521
题意概述:给出一个区间[L,R],统计区间中满足:1、4,8不同时出现;2、至少有3个相邻的相同数字 的数字个数。10^10<=L<R<10^11.
实际上要想个状态方程和转移很好想,只是细节和答案就有点。。。。
简单来说,设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<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 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;x<n[10];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;y<n[i-1]+(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<cnt;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<<calc(R)-calc(L)+check(L)<<'\n'; 91 return 0; 92 }