题解:洛谷 P1591 阶乘数码
【题目来源】
洛谷:P1591 阶乘数码 - 洛谷(luogu.com.cn)
【题目描述】
求 \(n!\) 中某个数码出现的次数。
【输入】
第一行为 \(t(t\le 10)\),表示数据组数。接下来 \(t\) 行,每行一个正整数 \(n(n\le 1000)\) 和数码 \(a\)。
【输出】
对于每组数据,输出一个整数,表示 \(n!\) 中 \(a\) 出现的次数。
【输入样例】
2
5 2
7 0
【输出样例】
1
2
【核心思想】
-
问题分析:给定 \(n\)(\(n \le 1000\))和一个数码 \(a\)(0~9),需要计算 \(n!\) 的十进制表示中数字 \(a\) 出现的次数。由于 \(n!\) 可能非常大(\(1000!\) 有约 2568 位),必须使用高精度乘法计算阶乘,然后逐位统计数码出现次数。
-
算法选择:
- 高精度乘法:使用数组
a[10000]存储阶乘的每一位(个位在a[1]),模拟手算乘法:从低位到高位依次乘以 \(i\),处理进位。 - 统计数码:计算完 \(n!\) 后,遍历数组的有效位,统计等于目标数码 \(a\) 的位数。
- 复杂度:每次阶乘计算 \(O(n^2)\),\(n \le 1000\),\(t \le 10\),完全可行。
- 高精度乘法:使用数组
-
关键步骤:
- 读入:读取 \(t\),对每组数据读取 \(n\) 和 \(a\)。
- 高精度乘法:
- 初始化
a[1] = 1,len = 1。 - 循环
i从 2 到 \(n\):carry = 0。- 对
j从 1 到len:tmp = a[j] * i + carry。a[j] = tmp % 10。carry = tmp / 10。
- 若
carry > 0,则继续将carry的每一位存入数组,并更新len。
- 初始化
- 统计:遍历
a[1..len],若a[j] == a,则count++。 - 输出:输出
count。
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2)\),\(n \le 1000\)。
- 空间复杂度:\(O(n)\)。
-
高精度与数码统计:
- 高精度乘法:逐位相乘并处理进位,是计算大数阶乘的标准方法。
- 数码统计:直接遍历数组统计,简单直接。
- 注意:\(n!\) 可能包含前导零?不,高精度乘法不会产生前导零。
- 适用场景:这类“大数阶乘 + 数码统计”问题,是高精度运算的入门应用。
【算法标签】
普及- #数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int a[10000], ans[10000]; // a数组用于存储阶乘的结果,ans数组用于存储每个测试用例的答案
int main()
{
int t, n, b; // t表示测试用例的数量,n表示阶乘的上限,b表示要统计的数字
cin >> t; // 输入测试用例的数量
// 处理每个测试用例
for (int k = 1; k <= t; k++)
{
ans[k] = 0; // 初始化当前测试用例的答案为0
cin >> n >> b; // 输入n和b
int x, len = 1; // x用于存储进位,len表示当前阶乘结果的位数
memset(a, 0, sizeof(a)); // 初始化a数组为0
a[1] = 1; // 初始化阶乘结果为1(1! = 1)
// 计算n的阶乘
for (int i = 1; i <= n; i++)
{
x = 0; // 初始化进位为0
for (int j = 1; j <= len; j++)
{
a[j] = a[j] * i + x; // 计算当前位的值(乘以i并加上进位)
x = a[j] / 10; // 计算新的进位
a[j] %= 10; // 取当前位的个位数
if (x != 0 && j == len) len++; // 如果最高位有进位,增加位数
}
}
// 统计阶乘结果中数字b出现的次数
for (int i = len; i >= 1; i--)
if (a[i] == b) ans[k]++; // 如果当前位等于b,答案加1
}
// 输出每个测试用例的答案
for (int i = 1; i <= t; i++)
cout << ans[i] << endl;
return 0;
}
【运行结果】
2
5 2
7 0
1
2
浙公网安备 33010602011771号