P1009 [NOIP 1998 普及组] 阶乘之和
P1009 [NOIP 1998 普及组] 阶乘之和
题目描述
用高精度计算出 \(S = 1! + 2! + 3! + \cdots + n!\)(\(n \le 50\))。
其中 ! 表示阶乘,定义为 \(n!=n\times (n-1)\times (n-2)\times \cdots \times 1\)。例如,\(5! = 5 \times 4 \times 3 \times 2 \times 1=120\)。
输入格式
一个正整数 \(n\)。
输出格式
一个正整数 \(S\),表示计算结果。
输入输出样例 #1
输入 #1
3
输出 #1
9
说明/提示
【数据范围】
对于 \(100 \%\) 的数据,\(1 \le n \le 50\)。
【其他说明】
注,《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 \(n \le 20\),使用书中的代码无法通过本题。
如果希望通过本题,请继续学习第八章高精度的知识。
这道题在类型上属于 高精度乘法 + 高精度加法的综合应用。
题目大意
计算 \(S = 1! + 2! + 3! + \dots + n!\),其中 \(n \le 50\),阶乘结果远超 64 位整数范围,必须手写高精度。
| 特征 | 说明 |
|---|---|
| 题型分类 | 高精度加法 + 高精度乘法的综合应用 |
| 核心知识点 | ① 高精度乘低精度(阶乘用) ② 高精度加高精度(求和用) |
| 与 P1303 的区别 | P1303 是高精度 × 高精度;本题阶乘只需高精度 × 低精度(乘数 ≤ 50),逻辑更简单 |
因为乘数 i(2~50)始终是普通整数,所以乘法时逐位乘 int 加进位即可,不用嵌套双循环,这也是本题与 P1601 加法模版结合的最佳切入点。
解决思路
-
拆成两个步骤:
- 高精度乘低精度:计算当前
i的阶乘fact = fact × i - 高精度加高精度:
sum = sum + fact
- 高精度乘低精度:计算当前
-
数组倒序存储(低位在前):
a[0]是个位,a[1]是十位,以此类推。
-
具体流程:
- 初始化
a[]和sum[]均为 1(即 1!)。 - 从
i = 2到n:- 调用高精度乘低精度,把
i乘进a[]。 - 调用高精度加高精度,把
a[]累加到sum[]。
- 调用高精度乘低精度,把
- 去掉前导零,逆序输出
sum[]。
- 初始化
方法一:基础高精度(数组模拟,通俗易懂)
#include <iostream>
using namespace std;
const int MAXN = 2000; // 50! 约 65 位,2000 很安全
int a[MAXN]; // 存放当前阶乘
int sum[MAXN]; // 存放最终和
// 高精度 × 低精度
void mul(int a[], int x) {
int carry = 0;
for (int i = 0; i < MAXN; ++i) {
carry += a[i] * x;
a[i] = carry % 10;
carry /= 10;
}
}
// 高精度 + 高精度
void add(int sum[], int a[]) {
int carry = 0;
for (int i = 0; i < MAXN; ++i) {
carry += sum[i] + a[i];
sum[i] = carry % 10;
carry /= 10;
}
}
int main() {
int n;
cin >> n;
a[0] = 1;
sum[0] = 1;
for (int i = 2; i <= n; ++i) {
mul(a, i);
add(sum, a);
}
// 逆序输出
int p = MAXN - 1;
while (p > 0 && sum[p] == 0) --p;
for (int i = p; i >= 0; --i)
cout << sum[i];
cout << endl;
return 0;
}
方法二:vector 版本(更安全,方便复用)
#include <iostream>
#include <vector>
using namespace std;
// 高精度 × 低精度
vector<int> mul(const vector<int>& A, int b) {
vector<int> C;
int carry = 0;
for (size_t i = 0; i < A.size(); ++i) {
carry += A[i] * b;
C.push_back(carry % 10);
carry /= 10;
}
while (carry) {
C.push_back(carry % 10);
carry /= 10;
}
return C;
}
// 高精度 + 高精度
vector<int> add(const vector<int>& A, const vector<int>& B) {
vector<int> C;
int carry = 0;
size_t i = 0;
while (i < A.size() || i < B.size() || carry) {
int x = (i < A.size() ? A[i] : 0);
int y = (i < B.size() ? B[i] : 0);
carry += x + y;
C.push_back(carry % 10);
carry /= 10;
++i;
}
return C;
}
int main() {
int n;
cin >> n;
vector<int> fact = {1}; // 当前阶乘
vector<int> sum = {1}; // 累加和
for (int i = 2; i <= n; ++i) {
fact = mul(fact, i);
sum = add(sum, fact);
}
// 逆序输出
for (int i = sum.size() - 1; i >= 0; --i)
cout << sum[i];
cout << endl;
return 0;
}
Python 版本
Python 自带大整数,直接硬算就能 AC。但手写结构可以帮助理解思路。
def mul(A, x):
C = []
carry = 0
for a in A:
carry += a * x
C.append(carry % 10)
carry //= 10
while carry:
C.append(carry % 10)
carry //= 10
return C
def add(A, B):
C = []
i = carry = 0
while i < len(A) or i < len(B) or carry:
x = A[i] if i < len(A) else 0
y = B[i] if i < len(B) else 0
carry += x + y
C.append(carry % 10)
carry //= 10
i += 1
return C
n = int(input())
fact = [1]
s = [1]
for i in range(2, n + 1):
fact = mul(fact, i)
s = add(s, fact)
print(''.join(str(x) for x in reversed(s)))
总结
| 内容 | 说明 |
|---|---|
| 题目类型 | 高精度加法 + 高精度乘法的综合应用 |
| 乘法特征 | 高精度 × 低精度(乘数 ≤ 50),比 P1303 的大数相乘简单 |
| 加法特征 | 与 P1601 逻辑完全一致,只多一个累加循环 |
| 核心优化 | 边乘边进位,处理好进位链 |
| 学习价值 | 高精度四则运算的综合性练习 |

浙公网安备 33010602011771号