BZOJ4521: [Cqoi2016]手机号码

省选2016系列…CQOI d1t3
显然的数位dp,状态也比较好想,dp[i][j][k][a][b][c]表示前i位,是否已经小于原数,当前后两个数是j,k,是否出现4,8,是否已经出现连续的3个。(有点复杂…>_<…)
省选数据是厉害…有三个数据的l都是10000000000,因为我默认他的位数是11位,l-1一下就挂飞了…大家引以为鉴…

#include
#include
#include
#include
#define ll long long
//by:MirrorGray
using namespace std;
const int N=13;
ll dp[N][2][N][N][4][2];

int g(int a,int b){
    return ((a==8)<<1)|(a==4)|((b==8)<<1)|(b==4);
}

ll solve(ll x){
    static int s[N];int top=0;
    while(x)s[top++]=x%10,x/=10;
    reverse(s,s+top);
    memset(dp,0,sizeof(dp));
    int ap=g(s[0],s[1]),fg=0;
    dp[2][0][s[0]][s[1]][ap][fg]=1;
    for(int i=1;i<=s[0];i++)for(int j=0;j<(i==s[0]?s[1]:10);j++)dp[2][1][i][j][g(i,j)][0]=1;
    for(int i=2;i<11;i++){
        ap|=g(s[i],s[i]);fg|=((s[i]==s[i-1])&&(s[i-1]==s[i-2]));
        if(fg!=3)dp[i+1][0][s[i-1]][s[i]][ap][fg]=1;
        for(int j=0;j<10;j++)
        for(int k=0;k<10;k++)
        for(int a=0;a<3;a++){
            for(int b=0;b<10;b++){
                int t1=a|g(b,b),t2=(b==k)&&(k==j);
                if(t1==3)continue;
                dp[i+1][1][k][b][t1][1]+=(b0][j][k][a][1]+dp[i][1][j][k][a][1];
                dp[i+1][1][k][b][t1][t2]+=(b0][j][k][a][0]+dp[i][1][j][k][a][0];
            }
        }
    }
    ll ret=0;
    for(int j=0;j<10;j++)
    for(int k=0;k<10;k++)
    for(int a=0;a<3;a++)
    for(int b=0;b<2;b++)ret+=dp[11][b][j][k][a][1];
    return ret;//+233;//(wu..)
}

int main(){
    ll l,r;scanf("%lld%lld",&l,&r);
    if(l==10000000000)printf("%lld\n",solve(r));//是厉害… 
    else printf("%lld\n",solve(r)-solve(l-1));
    return 0;
}
//orz Poker_face
//祈愿heoi2016一切平安…>_<… 

你可能感兴趣的:(dp,OIER的世界)