题解:洛谷 P1980 计数问题
【题目来源】
洛谷:P1980 [NOIP 2013 普及组] 计数问题 - 洛谷
【题目描述】
试计算在区间 \(1\) 到 \(n\) 的所有整数中,数字 \(x(0\le x\le 9)\) 共出现了多少次?例如,在 \(1\) 到 $11 $ 中,即在 \(1,2,3,4,5,6,7,8,9,10,11\) 中,数字 $1 $ 出现了 \(4\) 次。
【输入】
\(2\) 个整数 \(n,x\),之间用一个空格隔开。
【输出】
\(1\) 个整数,表示 \(x\) 出现的次数。
【输入样例】
11 1
【输出样例】
4
【核心思想】
-
问题分析:给定 \(n\) 和数字 \(x\),统计在 \(1 \sim n\) 的所有整数中,数字 \(x\) 出现的总次数。这是一个数位拆分 + 桶统计问题。
-
算法选择:
- 逐数拆分:遍历 \(1 \sim n\),对每个数进行数位拆分
- 桶数组计数:用
a[0..9]记录每个数字出现的次数,最后输出a[x]
-
关键步骤:
- 读入数据:
cin >> n >> x - 初始化:
a[0..9] = {0} - 遍历统计(\(i\) 从 \(1\) 到 \(n\)):
- 调用
calc(i)拆分数字 \(i\) 的每一位
- 调用
- 拆分函数
calc(x):- 当
x > 0时循环:a[x % 10]++:对最低位数字计数x /= 10:去掉最低位
- 当
- 输出结果:
a[x]
- 读入数据:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \log_{10} n)\),遍历 \(n\) 个数,每个数拆分 \(O(\log n)\) 次
- 空间复杂度:\(O(1)\),仅需大小为 10 的桶数组
-
数位统计的核心思想:
- 逐位剥离:
x % 10取最低位,x /= 10右移一位,循环直至数为 0 - 桶数组聚合:不关心数字出现在哪个数中,只关心每个数字的总出现次数
- 范围覆盖:遍历 \(1 \sim n\) 确保所有数字都被统计,无遗漏
- 适用于数位统计、频率统计、桶计数类入门问题
- 逐位剥离:
【算法标签】
入门 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, x;
int a[15]; //定义a数组,保存0-9数字的出现次数
void calc(int x)
{
while (x>0) { //进行数位拆分,对x每位上的数进行计数
a[x%10]++;
x /= 10;
}
}
int main()
{
cin >> n >> x; // 输入n和x
for (int i=1; i<=n; i++) { //从1遍历至n
calc(i); //对每个数进行统计
}
cout << a[x] << endl; //输出数字x出现的次数
return 0;
}
【运行结果】
11 1
4
浙公网安备 33010602011771号