手机号码 解题报告
题目描述
一眼数位dp,预处理很简单,但是统计逆天。
启示:只要不TLE和MLE,不要嫌状态多/麻烦
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
typedef long long ll;
int g[20][10];
long long L,R,f[20][11][20][2][2][2];//f[位数][首位][连续个数][有4][有8][有三连]
void init(){
for(int i=0;i<=9;i++) f[1][i][1][i==4][i==8][0]=1;
for(int i=2;i<=11;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
for(int l=1;l<i;l++)
for(int fr=0;fr<=1;fr++)
for(int et=0;et<=1;et++)
for(int sm=0;sm<=1;sm++)
f[i][j][(j==k)?(l+1):1][fr|(j==4)][et|(j==8)][sm|(((j==k)?(l+1):1)>=3)]+=f[i-1][k][l][fr][et][sm];
}
int a[20],cnt,Con[20];
bool Fr[20],Et[20],Sm[20];
ll ask(ll x){
if(x+1==1e10) return 0;
ll t=x,ans=0;
cnt=0;
while(t){a[++cnt]=t%10;t/=10;}
for(int i=1,j=11;i<j;i++,j--) swap(a[i],a[j]);
for(int i=1;i<a[1];i++)
for(int tim=1;tim<=11;tim++)
for(int fr=(i==4);fr<=1;fr++)
for(int et=(i==8);et<=1;et++)
if(fr+et!=2)
ans+=f[11][i][tim][fr][et][1];
for(int i=1;i<=11;i++) Fr[i]=(Fr[i-1]|(a[i]==4));
for(int i=1;i<=11;i++) Et[i]=(Et[i-1]|(a[i]==8));
for(int i=2;i<=11;i++) Sm[i]=(Sm[i-1]|(a[i]==a[i-1] && a[i-1]==a[i-2]));
for(int i=1;i<=11;i++) Con[i]=(a[i]==a[i-1]?(Con[i-1]+1):1);
for(int k=2;k<=11;k++)
for(int i=0;i<a[k];i++)
for(int tim=1;tim<=11-k+1;tim++)
for(int fr=(i==4);fr<=1;fr++)
for(int et=(i==8);et<=1;et++)
for(int sm=0;sm<=1;sm++){
if((fr|Fr[k-1])+(et|Et[k-1])!=2 && ((Sm[k-1]|sm)==1 || (i==a[k-1] && Con[k-1]+tim>=3)))
ans+=f[11-k+1][i][tim][fr][et][sm];
}
return ans+(Fr[11]+Et[11]!=2 && Sm[11]==1);
}
int main()
{
freopen("phone.in","r",stdin);
freopen("phone.out","w",stdout);
init();
scanf("%lld%lld",&L,&R);
printf("%lld",ask(R)-ask(L-1));
return 0;
}