【CQOI2016】手机号码

传送门:NKOJ3613
这是一道数位dp题。我最开始想用组合数学的方法(可以看看NKOJ1725)来做这道题,想了会儿不知道怎么写,才发现想复杂了。【CQOI2016】手机号码_第1张图片
很明显,L~R区间内满足条件的号码个数满足前缀和性质,那么我们所要求的答案即dp(R)-dp(L-1)。
无脑地定了一个7维的状态,如下:

f[i][j][k][g3][i4][i8][if_greater_than_x]
表示当前填第i个数字(从低位到高位填),上一个填入的数字是j,当前填入的数字连续出现的次数k,是否已经出现了至少3位相同的连续数字,是否出现过4,是否出现过8,构造的数字是否已大于x。

显然,合法的号码用f状态表示为:

f[11][i][j][1][0][0][0]
f[11][i][j][1][1][0][0]
f[11][i][j][1][0][1][0]
(0

统计答案时就把这些合法的方案都加上就行了。
状态定好了,转移方程也就出来了(我承认很丑):

if(j!=p)f[i+1][p][1][g3][i4|(p==4)][i8|(p==8)][if_greater(p,o,nub[i])]+=f[i][j][k][g3][i4][i8][o];
else f[i+1][p][k+1][g3|(k+1>2)][i4][i8][if_greater(p,o,nub[i])]+=f[i][j][k][g3][i4][i8][o];

这里没有管状态是否合法,直接就转移了,应该都能看懂吧,不懂的可以来怼我qwq(可私聊)。
以下是非常丑陋的代码:
这里写图片描述

#include
#include
#define LL long long
using namespace std;
LL L,R,len,ans;
LL f[15][15][15][2][2][2][2],nub[15];//f[i][j][k][if3][if4][if8][ifgreaterx]
bool if_greater(LL a,LL b,LL c)
{
    if(a>c)return 1;
    if(b&&(a==c))return 1;
    return 0;
}
LL dp(LL x)
{
    memset(f,0,sizeof(f));
    ans=len=0;
    while(x)
    {
        nub[len++]=x%10;
        x/=10;
    }
    for(int i=0; i<10; i++)
        f[1][i][1][0][i==4][i==8][nub[0]1;
    for(int i=1; i<=10; i++)
    {
        for(int j=0; j<10; j++)
        {
            for(int k=1; k<=i; k++)
            {
                for(int g3=0; g3<2; g3++)
                {
                    for(int i4=0; i4<2; i4++)
                    {
                        for(int i8=0; i8<2; i8++)
                        {
                            for(int o=0; o<2; o++)
                            {
                                for(int p=0; p<10; p++)
                                {
                                    if(j!=p)f[i+1][p][1][g3][i4|(p==4)][i8|(p==8)][if_greater(p,o,nub[i])]+=f[i][j][k][g3][i4][i8][o];
                                    else f[i+1][p][k+1][g3|(k+1>2)][i4][i8][if_greater(p,o,nub[i])]+=f[i][j][k][g3][i4][i8][o];
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    for(int i=1; i<10; i++)
    {
        for(int j=1; j<12; j++)
        {
            ans+=f[11][i][j][1][0][0][0]+f[11][i][j][1][1][0][0]+f[11][i][j][1][0][1][0];
        }
    }
    return ans;
}
int main()
{
    scanf("%lld%lld",&L,&R);
    if(L==1e10)printf("%lld",dp(R));
    else printf("%lld",dp(R)-dp(L-1));
    return 0;
}

你可能感兴趣的:(dp)