数位DP统计->数字计数(洛谷2602)

题意:统计区间内每个数字出现的次数

分析:前缀和差分,数位dp,从高到低考虑,有一种特殊情况是统计0出现的次数时,要考虑前导0

void solve(){
    long long l, r;
    cin >> l >> r;

    vector<int> num;
    int digit;

    vector<vector<long long>> dp(14, vector<long long>(14, -1));
    function<long long int(int, long long, bool, bool)> dfs = [&](int pos, long long sum, bool lead, bool limit) -> long long{
        if (pos == 0){
            return sum;
        }
        if (lead == false && limit == false && dp[pos][sum] != -1){
            return dp[pos][sum];
        }
        int up = (limit == true ? num[pos] : 9);
        long long res = 0;
        for (int i = 0; i <= up; ++i){
            if (lead == true && i == 0){
                res += dfs(pos - 1, sum, true, limit && i == up);       //当前pos考虑的数是0,而且前面所有已经考虑了的数都是0
            }
            else{
                res += dfs(pos - 1, sum + (i == digit), false, limit && (i == up));     //假设i=0,lead=false,说明当前的0不是前导0,可以进行计数了
            }
        }
        if (limit == false && lead == false){
            dp[pos][sum] = res;
        }
        return res;
    };

    auto cal = [&](long long x, int i)->long long{
        for (auto & x : dp){
            fill(x.begin(), x.end(), -1);
        }
        num.resize(1);
        while (x){
            num.emplace_back(x % 10);
            x /= 10;
        }
        return dfs(int(num.size()) - 1, 0, true, true);
    };

    for (int i = 0; i < 10; ++i){
        digit = i;
        cout << cal(r, i) - cal(l - 1, i) << ' ';
    }

}
posted @ 2024-01-15 10:52  _Yxc  阅读(28)  评论(0)    收藏  举报