又是一道裸的数位DP,只不过状态有点多(小萌新表示看着好晕)
warn限制最大值。last,llast枚举上一位、上上一位的数值。d4,d8分别表示4与8是否出现过。tri表示前面状态是否存在三连数,最后到达边界时只有合法的tri为真才能给值。
此题尤为坑的一点是当l=1e10时会发现减一以后变成了10位数,则第一位必然为0,导致记忆化搜索无法进行,所以需要特判。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long l,r,cnt,ans;
int s[12];
long long dp[12][12][12][2][2][2][2];//dp数组记得开long long
void init(long long x)
{
cnt=0;
while(x)s[++cnt]=x%10,x/=10;
}
long long Search(int len,int last,int llast,bool warn,bool d4,bool d8,bool tri)
{
long long &res=dp[len][last][llast][warn][d4][d8][tri];
if(res!=-1)return res;
if(len==0)return res=tri?1:0;
res=0;
for(int i=len==11?1:0;i<10;i++)
{
if(warn&&i>s[len])break;
bool nextd4=(d4||i==4),nextd8=(d8||i==8),nextwarn,nexttri;
if(nextd4&nextd8)continue;
nextwarn=warn&&i==s[len]?true:false;
nexttri=(tri||(llast==last&&last==i))?true:false;
res+=Search(len-1,i,last,nextwarn,nextd4,nextd8,nexttri);
}
return res;
}
int main()
{
scanf("%lld%lld",&l,&r);
memset(dp,-1,sizeof dp);
init(r);
ans+=Search(11,0,0,1,0,0,0);
memset(dp,-1,sizeof dp);
init(--l);
if(l>10000000000)ans-=Search(11,0,0,1,0,0,0);
//特判l==1e10
printf("%lld",ans);
return 0;
}