题目大意:要计算S=1!+2!+…+n!,其中n可以到50。
首先,我们需要想怎么实现高精度的阶乘和加法。高精度通常是用数组或者字符串来模拟大数的每一位,然后手动实现加法和乘法的运算。比如,阶乘的计算可以用一个数组来存储每一位数字,然后每次乘以当前的数,处理进位。而加法的话,同样需要逐位相加,处理进位。
那具体步骤应该是这样的:先算出每个i的阶乘,然后将它们累加起来。每次计算i!的时候,可以用之前的结果乘以i,这样就不用每次都从头开始计算,比如计算5!的时候,可以用4!的结果乘以5。这样会更高效一些。
然后,对于高精度乘法,比如一个数组表示的数和整数i相乘,需要每一位都乘以i,然后处理进位。例如,数组是[2, 1],代表12,乘以3的话,每一位乘3得到[6,3],然后处理进位,变成6和3,也就是36。如果有进位的话,比如[5, 2](25)乘以3,各位得到15和6,然后处理进位,15的十位1进到前面,变成5和6,加上进位后的结果就是75。
接下来,加法部分。每次计算完i!之后,需要将这个结果加到总和里。这里的高精度加法也需要处理进位的问题。例如,总和是[9],加上[1,2](21),得到[9+21=30],即[0,3],也就是30。
那具体怎么实现呢?可能需要用两个数组,一个存储当前的阶乘值,另一个存储总和。每次循环,先计算当前阶乘(即上一个阶乘乘以当前的i),然后加到总和中。
首先,如何表示大数?可以用一个vector<int>,每个元素代表一位数字,比如低位在前,或者高位在前。一般来说,低位在前处理进位比较方便,因为进位可以push_back到数组后面。比如,数字123用数组表示成[3,2,1],这样乘以某个数的时候,从低位开始乘,进位也自然处理到后面。
比如,计算阶乘的话,初始阶乘是1! = 1,数组是[1]。然后计算2!的时候,乘以2,得到[2]。3!的时候,乘以3,得到[6]。4!的话,乘以4,得到24,数组是[4,2]。以此类推。
然后是加法,总和初始为0,即空数组。每次将阶乘加到总和里。比如第一次加1!,总和变成1。第二次加2!,总和是3。第三次加6,总和是9,等等。
那具体实现的步骤应该是:
1. 初始化总和为0(一个空数组或者0的数组)。
2. 初始化当前阶乘为1(即数组[1])。
3. 对于i从1到n:
a. 将当前阶乘加到总和中。
b. 计算下一个阶乘,即当前阶乘乘以(i+1)。
4. 最后输出总和的数组表示。
不过,需要注意的是,每次的i对应的是i!,所以循环应该从1到n,计算i!,然后加到总和中。例如,当i=1时,阶乘是1!,加到总和;i=2时,阶乘是2!,加到总和,直到i=n。
但是,这样其实每次的阶乘计算应该是累积的。比如,当i=1时,当前阶乘是1!;然后i=2时,当前阶乘是1! *2=2!;i=3时,当前阶乘是2! *3=3!,依此类推。这样可以避免重复计算阶乘,提高效率。
那具体的代码结构应该是:
- 定义两个高精度数,一个存储当前阶乘(初始为1),另一个存储总和(初始为0)。
- 循环从1到n:
- 将当前阶乘加到总和里。
- 更新当前阶乘为当前阶乘乘以(i+1)?或者等一下,这里可能有问题。比如i=1的时候,当前阶乘是1!,加完到总和之后,下一个阶乘应该是2!,也就是当前阶乘乘以2。所以可能循环中的i应该代表当前处理的阶乘i!,然后下一个是i+1的阶乘。
或者,另一种方式:初始当前阶乘是1! = 1。然后,对于每个i从1到n,将当前阶乘加到总和,然后计算下一个阶乘(乘以i+1)?
比如,当i=1时,阶乘是1!,加到总和。然后计算2! =1! *2。然后i=2的时候,加2!到总和,计算3! =2! *3,依此类推。这样循环中的i从1到n,那么正确的做法是:
初始化current_fact = 1! (即1),sum = 0。
然后循环i从1到n:
sum += current_fact
current_fact *= (i+1)
但这样的话,i的取值到n的时候,current_fact是(n+1)!,这不对。因为current_fact应该代表i!,当i从1到n时,每次加的是current_fact,然后current_fact *= (i+1)得到(i+1)!。例如:
i=1时,current_fact是1!,加到sum。然后current_fact *=2,得到2!.
i=2时,加2!到sum,current_fact *=3得到3!.
这样循环结束后,i的值是n,current_fact是n!,但此时已经处理过i=1到n,所以sum是1!+2!+...+n!。这样应该没问题。
或者,另一种思路:current_fact初始为1(1!)。然后循环从1到n:
sum += current_fact
current_fact *= (i+1)
比如,当i=1的时候,sum加上1!,然后current_fact乘以2得到2!。当i=2的时候,sum加上2!,current_fact乘以3得到3!,以此类推,直到i=n的时候,sum加上n!,current_fact乘以(n+1),但此时循环结束。这样总共有n次加法,每次加的是1!, 2!, ...,n!,所以这样是正确的。
那这个思路是正确的。那现在,如何用高精度实现呢?
首先,current_fact和sum都是高精度数,需要用数组来存储每一位。例如,current_fact初始为[1],代表1。sum初始为空或者[0]。
每次加法的时候,sum = sum + current_fact。这需要高精度加法的实现。然后,current_fact = current_fact * (i+1)。这需要高精度乘以低精度的实现。
那现在,如何实现这两个操作?
高精度加法:
函数参数是两个数组,返回它们的和的数组。例如,假设两个数组都是低位在前,那么每一位相加,处理进位。
比如,a = [3,2,1](代表123),b = [5,4](代表45),相加得到[3+5=8, 2+4=6, 1+0=1],进位处理,结果正确吗?这里可能需要重新计算。比如,a是123,b是45,相加是168,即[8,6,1]。那数组的每一位相加,进位应该怎么处理?
正确的加法步骤是:
初始进位为0。对于每一位,sum = a_digit + b_digit + carry。当前位为sum%10,进位是sum/10。循环处理每一位,直到所有位数处理完,并且进位为0。
如果两个数的位数不同,较短的数在后面的位数视为0。
同样,乘法的话,高精度乘以一个整数。比如,数组表示的数和整数k相乘。每一位乘以k,加上进位,处理进位。例如,数组是[3,2,1](123),乘以2,每一位计算:
3*2=6,进位0 → 6
2*2=4+0=4 →4
1*2=2+0=2 →2
结果是[6,4,2] →246。即正确。
如果是进位的情况,比如[5, 8](85)乘以3:
5*3=15 →当前位5,进位1
8*3=24 +1=25 →当前位5,进位2
最后处理进位2 →添加进位到后面。所以结果是5,5,2 →即255。
所以,乘法函数需要处理每一位乘以k,加上进位,并处理进位。
那现在,我需要写出这两个函数:高精度加法、高精度乘低精度。
在代码中,可以用vector<int>来表示高精度数,低位在前。
那么,具体步骤如下:
1. 初始化sum为一个空数组或者0。例如,初始sum是空的,或者[0]。在加法的时候,需要考虑sum的初始值是否正确。
比如,当n=0时,sum是0。但题目中n>=1吗?题目中输入是一个正整数n,所以n>=1。所以sum的初始值可以是0,然后每次循环i从1到n,加current_fact到sum。
所以sum初始化为空或者[0]。然后在第一次加法的时候,sum = sum + current_fact(即0+1=1)。
或者,可以将sum初始化为空,然后在第一次加法时直接等于current_fact。但可能需要处理空的情况。所以可能更简单的是将sum初始化为0,即数组[0]。
那么,初始时current_fact是[1],sum是[0]。然后循环i从1到n:
sum = sum + current_fact
current_fact = current_fact * (i+1)
比如,当i=1的时候,sum变成0+1=1,然后current_fact乘以2得到2。当i=2时,sum加上2得到3,current_fact乘以3得到6。i=3时,sum+6=9,current_fact乘以4得到24。等等。
那现在,如何实现这些操作?
首先,加法函数。函数接收两个vector<int>,返回它们的和,低位在前。
vector<int> add(const vector<int>& a, const vector<int>& b) {
vector<int> res;
int carry = 0;
int max_len = max(a.size(), b.size());
for (int i=0; i < max_len; i++) {
int digit_a = i < a.size() ? a[i] : 0;
int digit_b = i < b.size() ? b[i] : 0;
int sum = digit_a + digit_b + carry;
res.push_back(sum % 10);
carry = sum / 10;
}
if (carry > 0) {
res.push_back(carry);
}
return res;
}
然后,乘法函数,接收一个vector<int>和一个整数k,返回它们的乘积,低位在前。
vector<int> multiply(const vector<int>& num, int k) {
vector<int> res;
int carry = 0;
for (int i=0; i < num.size(); i++) {
int product = num[i] * k + carry;
res.push_back(product % 10);
carry = product / 10;
}
while (carry > 0) {
res.push_back(carry % 10);
carry /= 10;
}
return res;
}
这样,这两个函数应该能处理高精度的加法和乘法。
现在,将这些函数整合到主函数中。
主函数的步骤:
- 输入n。
- 初始化sum为[0],current_fact为[1]。
- 循环i从1到n:
sum = add(sum, current_fact);
current_fact = multiply(current_fact, i+1);
- 输出sum的数组,高位在前。
比如,sum的数组是低位在前,所以输出的时候需要逆序。
例如,sum是[9] →输出9。如果是[8,6,2],则逆序输出268。
测试一下样例输入n=3的情况:
循环i=1到3:
i=1:
sum = add([0], [1]) →[1]
current_fact = multiply([1], 2) →[2]
i=2:
sum = add([1], [2]) →[3]
current_fact = multiply([2],3) →[6]
i=3:
sum = add([3], [6]) →[9]
current_fact = multiply([6],4) →[24]
循环结束,sum是[9],输出9。符合样例。
那这样代码应该正确。
但是,需要考虑n=50的情况,这时候sum会非常大,比如50!本身有几十位,加上其他阶乘的数,所以必须用高精度。
那现在,将这些函数写入C++代码。
可能的代码:
#include <iostream>
#include <vector>
using namespace std;
vector<int> add(const vector<int>& a, const vector<int>& b) {
vector<int> res;
int carry = 0;
int max_len = max(a.size(), b.size());
for (int i = 0; i < max_len; ++i) {
int digit_a = (i < a.size()) ? a[i] : 0;
int digit_b = (i < b.size()) ? b[i] : 0;
int sum = digit_a + digit_b + carry;
res.push_back(sum % 10);
carry = sum / 10;
}
if (carry > 0) {
res.push_back(carry);
}
return res;
}
vector<int> multiply(const vector<int>& num, int k) {
vector<int> res;
int carry = 0;
for (int i = 0; i < num.size(); ++i) {
int product = num[i] * k + carry;
res.push_back(product % 10);
carry = product / 10;
}
while (carry > 0) {
res.push_back(carry % 10);
carry /= 10;
}
return res;
}
int main() {
int n;
cin >> n;
vector<int> sum = {0}; // 初始总和为0
vector<int> current_fact = {1}; // 当前阶乘初始化为1! =1
for (int i = 1; i <= n; ++i) {
// 将当前阶乘加到总和中
sum = add(sum, current_fact);
// 计算下一个阶乘:(i+1)! = current_fact * (i+1)
current_fact = multiply(current_fact, i + 1);
}
// 输出结果,高位在前
for (int i = sum.size() - 1; i >= 0; --i) {
cout << sum[i];
}
cout << endl;
return 0;
}
那这个代码是否能处理n=50的情况?
是的,因为所有计算都是通过高精度进行的,没有溢出问题。但是,当n很大时,比如50,阶乘的位数会很大,但高精度数组可以处理。
测试一下n=3,输出应该是9。其他测试用例,比如n=1,输出1;n=2,输出3;n=4,1+2+6+24=33,输出33。
那这个就是正确的解法了。
AC代码:
#include <iostream>
#include <vector>
using namespace std;
// 高精度加法
vector<int> add(const vector<int>& a, const vector<int>& b) {
vector<int> res;
int carry = 0;
int max_len = max(a.size(), b.size());
for (int i = 0; i < max_len; ++i) {
int digit_a = (i < a.size()) ? a[i] : 0;
int digit_b = (i < b.size()) ? b[i] : 0;
int sum = digit_a + digit_b + carry;
res.push_back(sum % 10);
carry = sum / 10;
}
if (carry > 0) res.push_back(carry);
return res;
}
// 高精度乘法(大数 × 整数)
vector<int> multiply(const vector<int>& num, int k) {
vector<int> res;
int carry = 0;
for (int digit : num) {
int product = digit * k + carry;
res.push_back(product % 10);
carry = product / 10;
}
while (carry > 0) {
res.push_back(carry % 10);
carry /= 10;
}
return res;
}
int main() {
int n;
cin >> n;
vector<int> sum = {0}; // 初始化总和为0
vector<int> current_fact = {1}; // 当前阶乘初始化为1! = 1
for (int i = 1; i <= n; ++i) {
sum = add(sum, current_fact); // 累加当前阶乘
current_fact = multiply(current_fact, i + 1); // 计算下一个阶乘
}
// 从高位到低位输出结果
for (int i = sum.size() - 1; i >= 0; --i) {
cout << sum[i];
}
return 0;
}