P4124 [CQOI2016]手机号码(数位dp)

记录状态,pos当前位置,pre前一个数,ppre前前一个数,f是否有连续三个相等,f4是否出现4,f8是否出现8。
记忆化这些变量,保证状态不冲突,对于长度,因为固定了11位,所以不用记录了。
还有就是很坑的就是,虽然题目范围保证了数据都是11位,但如果当 l = 1 0 10 l = 10^{10} l=1010 ,l-1就只有10位了,所以必须得特判。

code

#pragma GCC optimize(2)
#include
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
#define ull unsigned ll
#define uint unsigned
#define pai pair
#define pal pair
#define IT iterator
#define pb push_back
#define fi first
#define se second
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);++i)
#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);--i)
#define endl '\n'
#define ll long long
const ll mod = 1e9+7;
int str[15];
ll dp[13][10][10][2][2][2];

ll dfs(int pos,int pre,int ppre,bool f,bool f4,bool f8,bool limit){
    if(f4&&f8)return 0;//剪枝,很明显没必要搜索下去了
    if(!pos){
        return f;
    }
    ll &x = dp[pos][ppre][pre][f][f4][f8];
    if(!limit&&x!=-1)return x;
    int up = limit?str[pos]:9;
    ll ans = 0;
    for(int i = 0;i <= up;++i){
        bool tpf = f||((pre==ppre)&&(pre==i));
        bool tpf4 = f4||(i==4);
        bool tpf8 = f8||(i==8);
        ans += dfs(pos-1,i,pre,tpf,tpf4,tpf8,limit&&i==up);
    }
    if(!limit)x = ans;
    return ans;
}

ll slove(ll x){
    int len = 0;
    while(x){
        str[++len] = x % 10;
        x/=10;
    }
    if(len!=11)return 0;
    ll res = 0;
    int up = str[len];
    for(int i =1;i<= up;i++){
    //    cout << "iL:" << i << endl;
        res += dfs(len-1,i,0,0,(i==4),(i==8),(i==up));
    }
    return res;
}

signed main() {
    #ifndef ONLINE_JUDGE
        //freopen("in.txt", "r", stdin);
       // freopen("out.txt","w",stdout);
    #endif
    memset(dp,-1,sizeof(dp));
    ll l,r;
    scanf("%lld%lld",&l,&r);
    printf("%lld\n",slove(r)-slove(l-1));
    return 0;
}

你可能感兴趣的:(luguo,dp,数学)