信息学奥赛一本通 2033:【例4.19】阶乘之和

【题目链接】

ybt 2033:【例4.19】阶乘之和

【题目考点】

1. 同余定理

根据同余定理,有:
( a ∗ b ) % m = ( a % m ∗ b % m ) % m (a*b)\%m = (a\%m * b\%m)\%m (ab)%m=(a%mb%m)%m
( a + b ) % m = ( a % m + b % m ) % m (a+b)\%m = (a\%m + b\%m)\%m (a+b)%m=(a%m+b%m)%m

2. 循环嵌套
3. 阶乘

n的阶乘为 n ! = 1 ∗ 2 ∗ . . . ∗ n n!=1*2*...*n n!=12...n
实现函数如下:

int fact(int n)
{
	int s = 1;
	for(int i = 1; i <= n; ++i)
		s *= i;
	return s;
}

【解题思路】

  • 一个直接的思路是:求出每个数的阶乘,加和,而后对M取模(设M的值为1000000),就可以得到结果的末6位。但阶乘的结果很大,会超出int以及long long类型可以表示的数字范围,所以必须一边计算一边取模。
    a n s = 1 ! + 2 ! + . . . + n ! ans = 1!+2!+...+n! ans=1!+2!+...+n!
    根据同余定理,有:
    a n s % M = ( 1 ! % M + 2 ! % M + . . . + n ! % M ) % M ans\%M = (1!\%M+2!\%M+...+n!\%M)\%M ans%M=(1!%M+2!%M+...+n!%M)%M
    n ! % M = ( n % M ∗ ( n − 1 ) ! % M ) % M n!\%M = (n\%M*(n-1)!\%M)\%M n!%M=(n%M(n1)!%M)%M
    可以先求每个阶乘模M的值,然后将这些结果加起来,一边加一边对M取模,即可。
  • 如果分别对每个数值求阶乘,需要做n次阶乘,每次阶乘的复杂度是 O ( n ) O(n) O(n),总体复杂度是 O ( n 2 ) O(n^2) O(n2),而本问题n达到 1 0 6 10^6 106 O ( n 2 ) O(n^2) O(n2)会超时。
  • 考虑到当 n ≤ M n\le M nM时: n ! % M = ( n ∗ ( n − 1 ) ! % M ) % M n!\%M = (n*(n-1)!\%M)\%M n!%M=(n(n1)!%M)%M,我们可以借助上一次的阶乘结果 ( n − 1 ) ! % M (n-1)!\%M (n1)!%M,乘上数字n再对M取模,即可得到 n ! % M n!\%M n!%M。这样求每个阶乘的复杂度是 O ( 1 ) O(1) O(1),总体复杂度是 O ( n ) O(n) O(n),可以通过。

【题解代码】

解法1:
#include<bits/stdc++.h>
using namespace	std;
#define M 1000000 //设置常量,下面M就是1000000
int	main()
{
	int n, num = 1, sum = 0;//num:阶乘%M sum:阶乘和%M 
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		num = num * i % M;
		sum = (sum + num) % M;
	}
	cout << sum;
	return 0;
}
### 关于信息学奥赛一本 1092 求阶乘的和 #### 题目描述 给定正整数 \( n \),计算并输出 \( S=1!+2!+\cdots+n! \)[^1]。 #### 解法思路 此问题的核心在于高精度计算。由于阶乘增长速度极快,即使是相对较小的 \( n \),其阶乘也会变得非常大,无法过常规的数据类型存储。因此,在实现时需采用数组来模拟多位数的加法与乘法操作[^2]。 对于每一个新的项 \( i! \),可以基于前一项 \( (i-1)! \) 来构建当前项的结果,并将其累加到总和中去。具体来说: - 初始化一个用于保存最终结果的大整数表示形式(常是一个列表),以及另一个临时变量用来保存每次新产生的阶乘值。 - 对于每一层循环中的 `i` 值,更新该临时变量以反映最新的阶乘值;接着将这个最新得到的阶乘加入到之前累积起来的结果里。 - 特别注意的是当处理每一位上的相加时可能会遇到进位的情况,所以要确保正确处理这些细节以便获得准确的答案。 下面是 Python 实现的: ```python def factorial_sum(n): result = [0]*1000 # 存储最后结果, 初始为全零 temp = [1] # 当前正在计算的阶乘初值设为1!, 即只有一位'1' for i in range(1, n + 1): carry = 0 # 进位初始化 # 更新temp至i! for j in range(len(temp)): product = temp[j] * i + carry temp[j] = product % 10 carry = product // 10 while carry != 0: temp.append(carry % 10) carry //= 10 # 将i! 加入result add_carry = 0 min_length = min(len(result), len(temp)) for k in range(min_length): sum_val = result[k] + temp[k] + add_carry result[k] = sum_val % 10 add_carry = sum_val // 10 if add_carry != 0 or len(temp) > len(result): diff_len = abs(len(result)-len(temp)) - int(add_carry==0) extra_digits = ([add_carry]+[0]*diff_len)[:max(diff_len, 0)+int(add_carry!=0)] result[min_length:min_length+len(extra_digits)] = extra_digits return ''.join(map(str, reversed([x for x in result if not(x == 0 and all(y == 0 for y in result[result.index(x):]))]))) ``` 这段代码实现了上述提到的方法论,能够有效地解决这个问题。需要注意的是这里为了简化逻辑,直接用了Python内置的大数支持特性作为辅助工具来进行验证而不是完全依赖手工实现的大数算法。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值