bzoj3134: [Baltic2013]numbers
稍微用脑子想一想,要是一个回文数,要么s[i]==s[i+1]要么s[i]==s[i+2]就可以实锤了
所以多开两维表示最近两位选的是什么数就完了
注意前导0
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int mbit=20; LL f[mbit][mbit][mbit];//第i位,第i位和第i-1位放的是啥 -----> 非回文数个数 (有前导零) LL g[mbit];//枚举到第i位 -----> 非回文数个数 (无前导零) void init() { g[1]=10,g[2]=91; for(int i=0;i<=9;i++) for(int j=0;j<=9;j++) if(i!=j)f[2][i][j]=1; for(int i=3;i<=18;i++) { for(int u=0;u<=9;u++) for(int v=0;v<=9;v++) if(u!=v) for(int w=0;w<=9;w++) if(u!=w&&v!=w) f[i][u][v]+=f[i-1][v][w]; g[i]=g[i-1]; for(int u=1;u<=9;u++) for(int v=0;v<=9;v++) g[i]+=f[i][u][v]; } } int clen,c[mbit]; LL getnum(LL k) { if(k<0)return 0; LL t=k;clen=0; while(t>0){c[++clen]=t%10;t/=10;} if(clen==0)clen++; c[clen+1]=-1;c[clen+2]=-1; LL ret=0; for(int i=clen;i>=1;i--) { int li=c[i]-1;if(i==1)li++; for(int j=0;j<=li;j++) { if(j==c[i+1]||j==c[i+2])continue; if(i==1)ret++; else if(i==2) { for(int k=0;k<=9;k++) if(k!=c[i+1]&&(k!=j||(i==clen&&j==0)))ret++; } else { if(i==clen&&j==0) { ret+=g[i-1]; continue; } for(int u=0;u<=9;u++) for(int v=0;v<=9;v++) if(u!=v&& u!=c[i+1]&&u!=j&&v!=j ) ret+=f[i-1][u][v]; } } if(c[i]==c[i+1]||c[i]==c[i+2])break; } return ret; } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); init(); LL L,R; scanf("%lld%lld",&L,&R); printf("%lld\n",getnum(R)-getnum(L-1)); return 0; }
pain and happy in the cruel world.