[Luogu P2602] [BZOJ 1833] [ZJOI2010]数字计数

本文详细解析洛谷BZOJ平台上一道关于计算指定范围内每个数字0-9出现次数的问题,通过预处理和逐位计算的方法,高效解决大范围数字统计难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

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

输入输出格式

输入格式:

输入文件中仅包含一行两个整数 a a a b b b,含义如上所述。

输出格式:

输出文件中包含一行 10 10 10个整数,分别表示 0 − 9 0-9 09 [ a , b ] [a,b] [a,b]中出现了多少次。

输入输出样例

输入样例#1:
1 99
输出样例#1:
9 20 20 20 20 20 20 20 20 20

说明

30 % 30\% 30%的数据中, a ≤ b ≤ 1 0 6 a\le b\le 10^6 ab106

100 % 100\% 100%的数据中, a ≤ b ≤ 1 0 12 a\le b\le 10^{12} ab1012

解题分析

我们一波手推可以发现, 考虑1位的情况下 0 ∼ 9 0\sim 9 09中每个数字出现了一次, 考虑2位的情况下(即把 5 5 5视为 05 05 05 0 ∼ 99 0\sim 99 099中每个数字出现了 20 20 20次,考虑 3 3 3位的情况下 0 → 999 0\to 999 0999中每个数字出现了 300 300 300次… 那么我们就可以预处理出这个玩意, 设为 t i m [ i ] tim[i] tim[i]

考虑 A B C D ABCD ABCD的情况, 首先所有数字作为后三位都出现了 ( A − 1 ) × t i m [ 3 ] (A-1)\times tim[3] (A1)×tim[3]次, 然后 &lt; A &lt;A <A的数位在第 4 4 4位出现了 1 0 3 10^3 103次, A A A出现了 B C D ‾ + 1 \overline {BCD}+1 BCD+1次。

这样我们就处理掉了第四位的情况, 直接继续处理第三位的情况即可。

但实际上我们计算了前导 0 0 0的情况, 例如 0999 , 0998 , 0997......0099 , 0098 , 0097..... , 0009 , 0008 , 0007..... 0999,0998,0997......0099,0098,0097.....,0009,0008,0007..... 0999,0998,0997......0099,0098,0097.....,0009,0008,0007.....所以我们需要将 0 0 0的个数减掉 1 0 3 + 1 0 2 + 1 0 1 + 1 0 0 10^3+10^2+10^1+10^0 103+102+101+100

这样最后的答案就是 s o l v e ( r i g ) − s o l v e ( l e f − 1 ) solve(rig)-solve(lef-1) solve(rig)solve(lef1)

代码如下:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 15
#define ll long long
ll tim[MX], pw[MX], cnt1[MX], cnt2[MX];
int buf[MX];
ll lef, rig;
void solve(ll now, ll *cnt)
{
    int len = 0; ll tmp = now;
    W (now) buf[++len] = now % 10, now /= 10;
    for (R int i = len; i; --i)
    {
        for (R int j = 0; j <= 9; ++j) cnt[j] += tim[i - 1] * buf[i];
        for (R int j = 0; j < buf[i]; ++j) cnt[j] += pw[i - 1];
        tmp -= buf[i] * pw[i - 1]; cnt[buf[i]] += tmp + 1;
        cnt[0] -= pw[i - 1];
    }
}
int main(void)
{
    scanf("%lld%lld", &lef, &rig);
    pw[0] = 1;
    for (R int i = 1; i <= 14; ++i) pw[i] = pw[i - 1] * 10, tim[i] = i * pw[i - 1];
    solve(lef - 1, cnt1);
    solve(rig, cnt2);
    for (R int i = 0; i <= 9; ++i) printf("%lld ", cnt2[i] - cnt1[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值