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<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#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]+=(b<s[i])*dp[i][0][j][k][a][1]+dp[i][1][j][k][a][1];
                dp[i+1][1][k][b][t1][t2]+=(b<s[i])*dp[i][0][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一切平安…>_<… 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值