按位差分统计区间数码个数。
OJ: roj
题目 ID: 1591
难度:普及-
标签:数位计数python
创建: 2026-09-30 20:38
更新: 2026-10-07 13:50
题目描述
给定正整数 1 99,输出 9 20 20 20 20 20 20 20 20 20。
思路
用
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 20:39
# update_at: 2026-09-30 20:39
import sys
def count_digits(n: int) -> list[int]:
"""统计 1..n 中每个数码出现的次数(n<=0 时全为 0),0 只统计真实数位上的。"""
cnt = [0] * 10
factor = 1 # 当前数位的位权:1 个位、10 十位、100 百位……
while factor <= n:
high, cur, low = n // (factor * 10), n // factor % 10, n % factor
# 1~9:按"当前位的数码 d 与 cur 的大小关系"把 [1,n] 分成三段计数
for d in range(1, 10):
if d < cur:
cnt[d] += (high + 1) * factor # 前缀 0..high 都能取满
elif d == cur:
cnt[d] += high * factor + low + 1 # 等值前缀只取到 low
else:
cnt[d] += high * factor # 前缀只能取 0..high-1
# 0:前缀不能为 0,否则该位是前导零、不是真实数位
if cur:
cnt[0] += high * factor # 当前位非 0,后缀可取满 factor 个
else:
cnt[0] += (high - 1) * factor + low + 1 # cur==0 时 high 至少为 1
factor *= 10
return cnt
def solve() -> None:
a, b = map(int, sys.stdin.buffer.read().split())
# 区间计数用前缀和相减:[a,b] = [1,b] - [1,a-1]
hi, lo = count_digits(b), count_digits(a - 1)
print(' '.join(str(x - y) for x, y in zip(hi, lo)))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 00:55
* update_at: 2026-10-06 00:55
*/
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
ll cnt[10]; // 1~n 中每个数码出现次数
// 计算 1~n 中各数码出现次数,结果存入全局 cnt[]
void count_up_to(ll n) {
for (int i = 0; i < 10; ++i) cnt[i] = 0;
if (n <= 0) return;
for (ll w = 1; w <= n; w *= 10) {
ll high = n / (w * 10);
ll cur = (n / w) % 10;
ll low = n % w;
// 数码 1~9:当前位直接按 high、cur、low 分三段计算
for (int d = 1; d <= 9; ++d) {
if (d < cur) cnt[d] += (high + 1) * w;
else if (d == cur) cnt[d] += high * w + low + 1;
else cnt[d] += high * w;
}
// 数码 0:跳过前导零,高位前缀从 1 开始
if (cur == 0) cnt[0] += (high - 1) * w + low + 1;
else cnt[0] += high * w;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll a, b;
if (!(cin >> a >> b)) return 0;
ll ans[10];
count_up_to(b);
for (int i = 0; i < 10; ++i) ans[i] = cnt[i];
count_up_to(a - 1);
for (int i = 0; i < 10; ++i) {
ans[i] -= cnt[i];
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
return 0;
}