数字计数(洛谷P2602)

数字计数(洛谷P2602)

题目大意

给定两个正整数 ab,求在 [a,b] 中的所有整数中,每个数码(digit)各出现了多少次。(1a,b1012)

题解

数位dp(dfs版)

以求1的数量为例,数字9对1数量的贡献为1,数字99对1的贡献呢,十位数上对1的贡献有10,11...19,也就是10的贡献。个位数上对1的贡献有11,21..91。也就是说每个十位会有一次个位上的贡献,本来应该会有01,但是有前导零,所以没有贡献,那么999呢,百位上有100,101...199,有100的贡献,同时每个百位都会有都会有一次十位上的贡献,就会有9次,以此类推,我们会发现,如果不管前导零,个位数放满的贡献是 1 ,十位数放满的贡献为 10+ ... 。所以我们有以下dp方程:

dp[i]=dp[i1]10+10i1

dp[i1]10 即为前 i1 位的贡献,10i1 为这一位的贡献。

以上都是在每个数位都满的情况进行的讨论,假如我们要对 [1,5023] 数一数2的数量,首先对于千位上的1、2、3、4这些,个十百都是可以放满的,即1999,2999,3999,4999。而到了5这里,只有 [5000,5023] ,这便是到了上界,是不能放满的。所以,我们记忆化搜索时,可以记忆无前导零且没有到达上界的位的贡献,如我们记录千位上的1的贡献时,单独考虑千位的贡献,记忆化后面的 [0,999] ,那么千位是2、3、4时可以直接找到后3位的贡献,同时5因为到达上界需单独考虑。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using ll = long long;

ll f[15], mi[15], p[15], now[15];
    // 位数, 数字,  有无前导零, 是否达到上界
ll dfs(int pos, int d, bool lead, bool lim){
    if(pos == 0)return 0;
    //无前导零并且没达到上界
    if(!lead && !lim && (~f[pos]))return f[pos];    //判断是否可以记忆化,如果f[pos] = -1,则~f[pos]=0,不满足记忆化。
    ll cnt = 0;
    int up = lim ? p[pos] : 9;  //判断可不可以放满
    for(int i = 0; i <= up; ++i){
        if(lead && i == 0){ //有前导零
            cnt += dfs(pos - 1, d, 1, lim && i == up);
        }else if(i == d && lim && i == up){ //是需要计算答案的数字且达到了上界
            cnt += now[pos - 1] + 1 + dfs(pos - 1, d, 0, 1);
        }else if(i == d){   //没有到达上界,
            cnt += mi[pos - 1] + dfs(pos - 1, d, 0, 0);
        }else{
            cnt += dfs(pos - 1, d, 0, lim && i == up);
        }
    }

    if(!lead && !lim)f[pos] = cnt; //无前导零且没有到达上界,记忆化
    return cnt;
}

ll calc(ll a, int d){
    int len = 0;
    memset(f, -1, sizeof(f));
    while(a){
        p[++len] = a % 10;
        a /= 10;
        now[len] = now[len - 1] + p[len] * mi[len - 1]; //主要用来处理上界
    }
    return dfs(len, d, 1, 1);
}

int main(void){
    ll a, b;
    scanf("%lld%lld", &a, &b);
    mi[0] = 1;
    for(int i = 1; i <= 12; ++i)mi[i] = mi[i - 1] * 10; //预处理10的幂次
    for(int i = 0; i < 10; ++i){
        printf("%lld ", calc(b, i) - calc(a - 1, i));
    }
    return 0;
}

posted @   言葉の庭  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示