阶乘质因数分解
\(1 \leq n \leq 10^6\), 唯一分解(质因数分解) \(n!\),输出 \(p_i,c_i\)。
思路
前置知识:线性筛 (质数判定的算法4)。
显然 \(n!\) 的每个质因子都小于等于 \(n\)。
因为 \(n! = n(n-1)(n-2)(n-3)\cdots 3 \cdot 2 \cdot 1\),所以质数 \(p\) 在 \(n!\) 出现的次数为 \(p\) 在 \(1\sim n\) 出现的次数。
对于 \(i=1,2,3\dots n\):
对于 \(p\):若有 \(p|i\),则 \(p\) 出现次数+1
对于 \(p^2\):若有 \(p^2|i\),则 \(p\) 出现次数 +1,而不是 +2,因为其中一个 \(p\) 在 \(p|i\) 时被统计了。
对于 \(p^3\):若有 \(p^3|i\),则 \(p\) 出现次数 +1,而不是 +3,因为其中一个 \(p\) 在 \(p|i\) 时被统计了,另一个 \(p\) 在 \(p^2|i\) 时被统计了。
...
则 \(p\) 在 \(n!\) 中出现的次数为 \(\lfloor\frac{n}{p}\rfloor + \lfloor\frac{n}{p^2}\rfloor + \lfloor\frac{n}{p^3}\rfloor + \cdots + \lfloor\frac{n}{p^{\lfloor\log_p{n}\rfloor}}\rfloor = \sum\limits_{p^k\leq n}{\lfloor\frac{n}{p^k}\rfloor}\)。
综上有以下步骤:
- 线性筛求 \(1 \sim n\) 内的质数。\(O(n)\)
- 遍历 \(1 \sim n\) 内的质数,找出质数\(p\),\(p|n\)。\(O(\pi(n))=O(\frac{n}{\log n})\)
- 对于质数 \(p\),求出在 \(n!\) 中出现的次数。\(O(\log n)\)
\(\pi(x)\) 是小于等于 \(x\) 的质数个数,约等于 \(\frac{n}{\log n}\)
综上时间复杂度为 \(O(n + \frac{n}{\log n} \cdot \log n) = O(n)\)。
代码实现
int n;
long long v[N];
::std::vector<int> ps;
int main() {
scanf("%d", &n);
/* 线性筛 */
for(int i = 2; i <= n; i++)
{
if(!v[i])
v[i] = i, ps.push_back(i);
for(int j : ps)
if(v[i] < j || j > n / i) break;
else v[i * j] = j;
}
for(int i : ps)
{
ll p = i;
int c = 0;
while(p <= n)
{
c += n / p;
p *= i;
}
printf("%d %d\n", i, c);
}
return 0;
}