题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(n\)\(n \le 1000\))和一个数码 \(a\)(0~9),需要计算 \(n!\) 的十进制表示中数字 \(a\) 出现的次数。由于 \(n!\) 可能非常大(\(1000!\) 有约 2568 位),必须使用高精度乘法计算阶乘,然后逐位统计数码出现次数。

  2. 算法选择

    • 高精度乘法:使用数组 a[10000] 存储阶乘的每一位(个位在 a[1]),模拟手算乘法:从低位到高位依次乘以 \(i\),处理进位。
    • 统计数码:计算完 \(n!\) 后,遍历数组的有效位,统计等于目标数码 \(a\) 的位数。
    • 复杂度:每次阶乘计算 \(O(n^2)\)\(n \le 1000\)\(t \le 10\),完全可行。
  3. 关键步骤

    • 读入:读取 \(t\),对每组数据读取 \(n\)\(a\)
    • 高精度乘法
      • 初始化 a[1] = 1len = 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
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\)\(n \le 1000\)
    • 空间复杂度:\(O(n)\)
  5. 高精度与数码统计

    • 高精度乘法:逐位相乘并处理进位,是计算大数阶乘的标准方法。
    • 数码统计:直接遍历数组统计,简单直接。
    • 注意\(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
posted @ 2026-08-31 09:25  团爸讲算法  阅读(9)  评论(0)    收藏  举报