惠州网页模板建站企业网站设计公司
阶乘分解题解
题目传送门
197. 阶乘分解
一、题目描述
给定整数N,将阶乘N!分解质因数,按照算术基本定理的形式输出分解结果中的质因数pᵢ和对应的指数cᵢ。
二、题目分析
- 需要将N!表示为质数的幂次乘积形式
- 关键在于高效计算每个质数在N!中的出现次数
- 需要先找出所有≤N的质数
三、解题思路
- 使用欧拉筛法预处理所有≤N的质数
- 对于每个质数p,计算在N!中的次数:
- 计算⌊N/p⌋ + ⌊N/p²⌋ + ⌊N/p³⌋ + … 直到pᵏ > N
- 输出所有质数及其对应的次数
四、算法讲解
欧拉筛法(线性筛)
- 原理:每个合数只被它的最小质因数筛掉
- 时间复杂度:O(n)
- 实现步骤:
- 遍历2到N的每个数i
- 如果i未被标记,则是质数,加入质数表
- 用当前数i与已知质数相乘标记合数
- 当i能被质数整除时停止标记
阶乘质因数分解
- 勒让德公式:计算质数p在N!中的次数为:
∑⌊N/pᵏ⌋ (k=1,2,…直到pᵏ>N) - 例子:计算5!中2的指数
⌊5/2⌋=2, ⌊5/4⌋=1 → 2+1=3
五、代码实现
#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1e6 + 10;
int prime[N], cnt; // 质数表和计数器
int st[N]; // 标记是否为合数
int n;// 欧拉筛法预处理质数
void init(int n)
{for (int i = 2; i <= n; i ++){if(!st[i]) // i是质数prime[cnt ++] = i; // 加入质数表// 用当前数和质数表中的数标记合数for (int j = 0; prime[j] * i <= n; j ++){st[i * prime[j]] = 1; // 标记合数if(i % prime[j] == 0) break; // 保证每个合数只被最小质因数筛掉}}
}void solve()
{cin >> n;init(n); // 预处理≤n的所有质数// 对每个质数计算在n!中的次数for (int i = 0; i < cnt; i ++){int p = prime[i]; // 当前质数int s = 0; // 计数器// 计算p在n!中的次数:n/p + n/p² + n/p³ + ...for (int j = n; j ; j /= p){s += j / p;}cout << p << " " << s << "\n"; // 输出质数和次数}
}signed main()
{ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);solve();return 0;
}
六、重点细节
- 筛法范围:只需要筛到N,因为N!的质因数不会超过N
- 次数计算:使用勒让德公式,通过不断除以p来计算总次数
- 效率优化:欧拉筛法保证质数预处理高效,次数计算也是对数级别
- 边界处理:N≥3,无需处理特殊情况
七、复杂度分析
- 时间复杂度:
- 预处理质数:O(n)
- 计算每个质数的次数:O(cnt * logₚN) ≈ O(n/lnn * lnn) = O(n)
- 空间复杂度:O(n),用于存储质数表和标记数组
八、总结
本题通过欧拉筛法高效预处理质数,再应用勒让德公式计算每个质数在阶乘中的次数,实现了对阶乘的质因数分解。算法结合了数论中的筛法和阶乘性质,在O(n)时间复杂度内解决问题,是质因数分解的经典应用。