题面:Luogu3413
这好像是我写的第一篇数位DP的blog吧。。。
半原创吧,参考的是我的同学lc233的blog:传送门
首先看到这个数据范围和样例就可以知道这题的主体思路了吧。
我们可以定义状态
f[i][j][k]
表示位数为i的,最高位为j,次高位为k的萌数个数。
一开始推的话很简单:
首先回文串嘛>2就好啦
所以我们只要判断2情况
aa或者aba; ——lc233
所以我们只要考虑连续两位的问题就好了。
所以接下来考虑统计答案的问题了。我们发现这个状态会有很多重复,很麻烦。
那么我们把
f[i][j][k]
定义为不是萌数的个数,这就好办多了。
接下来就是常规数位DP的写法了。。。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
#define int long long
using namespace std;
const int MOD=1e9+7;
int f[1010][10][10];//注意这里f定义:这些数中不是萌数的个数,别搞错
string l,r;
inline int dp(string x){
int l=x.size(),n=0,ans=0;
for(int i=0;i<l;i++)n=(n*10+x[i]-'0')%MOD;n=(n==MOD)?0:n+1;
for(int i=2;i<l;i++)
for(int j=1;j<10;j++)
for(int k=0;k<10;k++)(ans+=f[i][j][k])%=MOD;
if(l>1)(ans+=10)%=MOD;//0~9
int la=-1,lla=-1;bool flag=1;
for(int i=0;i<l-1;i++){
int now=x[i]-'0';
for(int j=0;j<now;j++)if(i!=0||j!=0)
for(int k=0;k<10;k++)if(la!=j&&lla!=j&&j!=k&&k!=la)(ans+=f[l-i][j][k])%=MOD;
if(now==la||now==lla){flag=0;break;}
lla=la;la=now;
}
if(flag)for(int j=0;j<=x[l-1]-'0';j++)if(j!=la&&j!=lla)ans=(ans==MOD)?0:ans+1;
return (n-ans+MOD)%MOD;
}//整个统计答案过程不多解释了
signed main()
{
ios::sync_with_stdio(0);
for(int i=2;i<=1000;i++)
for(int j=0;j<10;j++)
for(int k=0;k<10;k++)if(j!=k){
for(int l=0;l<10;l++)if(j!=l&&k!=l)(f[i][j][k]+=f[i-1][k][l])%=MOD;//如果没有出现回文现象加上去
if(i-1==1)f[i][j][k]=(f[i][j][k]==MOD)?0:f[i][j][k]+1;//从1开始
}
cin>>l>>r;
int ans=(dp(r)-dp(l)+MOD)%MOD;int L=l.size();
for(int i=1;i<L;i++)if(l[i]==l[i-1]||i>1&&l[i]==l[i-2]){ans++;break;}//上面的差分是l+1~r的,这里把l加上去
cout<<ans<<endl;
return 0;
}