【[CQOI2016]手机号码】

递推版的数位dp

绝对的暴力美学

我们设\(dp[l][i][j][0/1][0/1][0/1]\)表示到了第\(l\)位,这一位上选择的数是\(i\)\(l-1\)位选择的数是\(j\),第一个\(0/1\)代表\(4\)没有/有出现过,第二个\(0/1\)代表\(8\)没有/有出现过,第三个\(0/1\)代表连续三位没有/有出现过

于是转移很简单了

但是卡位实在是鬼畜

我卡位的方式有些鬼畜,所以细节非常的多

#include
#include
#include
#define re register
#define maxn 16
#define LL long long
LL L,R;
int a[maxn],num;
LL dp[maxn][11][11][2][2][2];
//位数,这一位上的数,上一位的数,0/1表示4/8有/没有出现,0/1表示有/没有连续三位 
inline LL slove(LL x)
{
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    num=0;
    while(x)
    {
        a[++num]=x%10;
        x/=10;
    }//分解数位
    a[num+1]=-11,a[num+2]=11;
    for(re int i=0;i<=9;i++)
        for(re int j=0;j<=9;j++)
            for(re int k=0;k<=9;k++)
            {
                int opt_4=0,opt_8=0;
                if(i==4||j==4||k==4) opt_4=1;
                if(i==8||j==8||k==8) opt_8=1;
                if(i==j&&j==k) dp[3][i][j][opt_4][opt_8][1]+=1;
                else dp[3][i][j][opt_4][opt_8][0]+=1;
            }//先初始化dp[3]之后再往下推
    for(re int l=3;l=3;l--)//卡位,这里保证从[l+1,num]和给定数是完全相等的
    {
        if(o4&&o8) break;
        for(re int i=0;ia[2]*10+a[1]) continue;//不能超过给定数的最后两位
                if(o4&&o8) continue;
                if(i==4||j==4)
                    if(o8) continue;//有4就不能有8
                if(i==8||j==8)
                    if(o4) continue;//有8就不能有4
                if(i==4&&j==8) continue;
                if(i==8&&j==4) continue;//更不可能同时出现
                if(o||(i==j&&j==a[3])||(i==a[3]&&a[3]==a[4])) ans++;
                //最后两位仍有可能和前面构成三位连续
            }
    return ans;
}
int main()
{
    scanf("%lld%lld",&L,&R);
    printf("%lld\n",slove(R)-slove(L-1));
    return 0;
}

你可能感兴趣的:(【[CQOI2016]手机号码】)