题解:洛谷 P1009 阶乘之和

【题目来源】

洛谷:P1009 [NOIP 1998 普及组] 阶乘之和 - 洛谷

【题目描述】

用高精度计算出 \(S=1!+2!+3!+\dots+n!(n\le 50)\)

其中 ! 表示阶乘,定义为 \(n!=n\times(n-1)\times (n-2)\times \dots \times 1\)。例如,\(5!=5\times 4\times 3\times 2\times 1=120\)

【输入】

一个正整数 \(n\)

【输出】

一个正整数 \(S\),表示计算结果。

【输入样例】

3

【输出样例】

9

【核心思想】

  1. 问题分析:给定 \(n \le 50\),计算 \(S = 1! + 2! + \cdots + n!\)。由于 \(50!\) 远超 64 位整数范围,必须使用高精度运算(大整数模拟)。

  2. 算法选择

    • 高精度乘法:用数组倒序存储大整数,模拟竖式乘法计算阶乘
    • 高精度加法:逐位累加每个阶乘到结果数组
    • 倒序存储:数组下标 \(0\) 存个位,便于进位处理
  3. 关键步骤

    • 读入数据cin >> n
    • 初始化a[0] = 1, res[0] = 1\(1! = 1\)
    • 循环计算\(i\)\(2\)\(n\)):
      • 计算 \(i!\)(高精度乘法 \(a \times i\)
        • 遍历 \(j\)\(0\)\(1010\)
          • a[j] = carry + a[j] * i:当前位乘 \(i\) 加进位
          • carry = a[j] / 10:计算新进位
          • a[j] %= 10:保留个位
      • 累加阶乘到结果(高精度加法 \(res += a\)
        • 遍历 \(j\)\(0\)\(1010\)
          • res[j] += a[j]:逐位相加
          • res[j+1] += res[j] / 10:处理进位
          • res[j] %= 10:保留个位
    • 去除前导零:从高位向低位找第一个非零数字确定长度
    • 倒序输出:从高位到低位输出结果
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot L)\)\(L = 1010\) 为数组长度,每次乘法和加法均为 \(O(L)\)
    • 空间复杂度:\(O(L)\),两个高精度数组
  5. 高精度运算的核心思想

    • 数组模拟大整数:用数组每一位存一个十进制数字,下标 \(0\) 为个位,便于从低位到高位处理进位
    • 逐位乘加:模拟小学竖式运算,当前位 = 当前位 \(\times\) 乘数 + 进位,新进位 = 当前位 / 10
    • 即时进位:乘法和加法后立即处理进位,避免溢出
    • 结果复用\(a\) 数组保存当前 \(i!\),下次循环直接乘 \((i+1)\)\((i+1)!\),无需重新计算
    • 适用于大整数阶乘、高精度累加、数位模拟类问题

【算法标签】

普及- #数学

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

// 定义全局数组存储阶乘和结果
int a[1010];             // 存储当前阶乘值(倒序存储)
int res[1010];           // 存储阶乘累加和(倒序存储)

/**
 * 主函数 - 程序入口
 * @return 程序执行状态码(0表示成功)
 */
int main()
{
    int n;               // 定义变量:计算到n的阶乘和
    cin >> n;            // 输入n值

    // 初始化数组(1! = 1)
    a[0] = 1;            // 个位存储1
    res[0] = 1;          // 初始和为1

    // 计算2!到n!的阶乘及其累加和
    for (int i = 2; i <= n; i++)
    {
        // 计算i的阶乘(a = a * i)
        int carry = 0;    // 进位值初始化为0
        for (int j = 0; j < 1010; j++)
        {
            a[j] = carry + a[j] * i;  // 当前位乘i加上进位
            carry = a[j] / 10;        // 计算新的进位
            a[j] %= 10;               // 保留个位数
        }

        // 计算1!+2!+...+i!(res += a)
        for (int j = 0; j < 1010; j++)
        {
            res[j] += a[j];           // 逐位相加
            res[j+1] += res[j] / 10;  // 处理进位
            res[j] %= 10;             // 保留个位数
        }
    }

    // 计算实际数字长度(去除前导零)
    int len = 1010;
    while (0 == res[len-1] && len > 1)
    {
        len--;            // 从高位向低位查找第一个非零数字
    }

    // 倒序输出结果(高位在前)
    for (int i = len-1; i >= 0; i--)
    {
        cout << res[i];   // 输出每一位数字
    }

    return 0;            // 程序正常结束
}

【运行结果】

3
9
posted @ 2026-08-05 15:44  团爸讲算法  阅读(44)  评论(0)    收藏  举报