• 855E


    数位DP

    昨天的B题,excited

    又学习了一下数位dp...

    数位dp要考虑几个比较重要的东西:1.前导0,2.天际线,3.记忆化的条件,4.细节

    经常数位dp会问我们l->r区间中满足某某条件的数的个数,这个条件很明显满足可减性,所以一般转化为1->r的数-1->l-1的数,采用记忆化搜索的方式统计

    这道题状态为dp[base][bit][state][pre],表示当前底数为base,统计的数有bit位,每种数出现次数用state表示,有没有前导0

    然后考虑状态的改变,也就是前导0,天际线的变化,先考虑前导0的变化,如果当前放置的数不是0,那么前导0不存在,可以把这个数放进状态内,否则如果一直是前导0则不把前导0放入状态,前导0也要设进dp状态内;然后考虑天际线,我们先想一想所设的dp状态,dp[base][bit][state][pre],这是表示当前base下,这个数包括前导0,状态为state,有没有前导0,注意这个数是包括所有位数为bit的数,如果我们在记忆化搜索的时候碰到了天际线,也就是limit=1,那么我们不能对搜出的结果记忆化,也不能返回之前已经记忆化好的答案,因为天际线去除掉了一些数,而dp统计了所有长度为bit的数。

    这样能够保证所有的数都被统计进去吗?是可以的,我们统计了天际线及以下长度为bit的数,但是那些长度小于bit的呢?其实我们把这些数转换为了加上前导零的数,这样就能很好地统计了。

    所以数位dp我们要考虑1.前导0的变化,2.是否卡在天际线,3.dp状态不同情况下的变化,4.记忆化的条件,5.每次枚举第bit位是什么的范围,然后通过记忆化搜索实现。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int q;
    ll dp[11][70][2048][2], f[20];
    ll dfs(int base, int bit, int S, int pre, int limit) //flag = 2 超了 flag = 1正好 flag = 0 小了 
    {
        if(bit == 0) return !S;
        if(!limit && dp[base][bit][S][pre] != -1) return dp[base][bit][S][pre];
        ll ret = 0;
        int mx = limit ? f[bit] : base - 1;
        for(int i = 0; i <= mx; ++i) 
        {
            if(i == 0 && pre) ret += dfs(base, bit - 1, S, pre, limit && (i == mx));
            else ret += dfs(base, bit - 1, S ^ (1 << i), 0, limit && (i == mx));
        }
        if(!limit) dp[base][bit][S][pre] = ret;
        return ret;
    }
    ll solve(int base, ll lim)
    {
        f[0] = 0;
        while(lim) 
        {
            f[++f[0]] = lim % base;
            lim /= base;
        }
        return dfs(base, f[0], 0, 1, 1);
    }
    int main()
    {
        memset(dp, -1, sizeof(dp));
        scanf("%d", &q);
        while(q--)
        {
            int base;
            ll l, r;
            scanf("%d%lld%lld", &base, &l, &r);
            printf("%lld
    ", solve(base, r) - solve(base, l - 1));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【python练习】选课系统(基于面向对象的)
    【面向对象】类的特殊成员方法
    【面向对象】属性方法
    【面向对象】类方法
    【面向对象】静态方法
    【python练习】计算器(使用正则表达式实现)
    【面向对象】多态
    【面向对象】新式类和旧式类的区别
    re.findall()正则表达式中括号的坑
    您真的了解javaScript?(2)
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7594172.html
Copyright © 2020-2023  润新知