D. Storming Arasaka
题意简述
给定一个整数 n。考虑 n 的所有正因子,除了 1 以外都要分到若干层 L_1, L_2, ..., L_k。
分层需要满足:
- 如果因子
x在第i层,那么x的所有真因子,除了1和x自己以外,都必须出现在更早的层; - 每一层里的数可以排成一个序列,使得相邻两个数的
gcd都大于1。
求最少需要多少层。
记号
设:
text
n = p_1^{e_1} * p_2^{e_2} * ... * p_m^{e_m}
定义:
text
Ω(n) = e_1 + e_2 + ... + e_m
表示质因子总个数,重复计算。
定义:
text
ω(n) = m
表示不同质因子的个数。
本题答案是:
text
Ω(n) + ω(n) - 1
为什么至少需要这么多层
1. 不同质数不能放在同一层
比如质数 2 和 3。
它们的 gcd(2, 3) = 1,不能相邻。
更重要的是,如果某层里有质数 p,那么这一层不能再有任何 p 的倍数。因为那些倍数的真因子包含 p,按规则必须放在更早的层。
所以含有质数 p 的层只能放 p 自己。
因此所有不同质数必须分别占用一层:
text
至少需要 ω(n) 层来放这些质数
设最后出现的那个质数在第 L 层,那么一定有:
text
L >= ω(n)
2. 从这个质数走到 n 还要继续往后放
假设最后出现的质数是 p。
从 p 开始,可以不断乘上剩余的质因子,形成一条因子链:
text
p -> p * q_1 -> p * q_1 * q_2 -> ... -> n
这条链一共有 Ω(n) 个数。
因为后一个数以前一个数为真因子,所以它必须在更晚的层。
如果 p 在第 L 层,那么 n 至少要在:
text
L + Ω(n) - 1
层。
又因为 L >= ω(n),所以:
text
答案 >= Ω(n) + ω(n) - 1
为什么这么多层一定够
我们构造一种分层方法。
第一步,把所有不同质数分别单独放在前 ω(n) 层。
例如:
text
L_1 = {p_1}
L_2 = {p_2}
...
L_m = {p_m}
第二步,从 s = 2 到 Ω(n),把所有满足下面条件的因子放进同一层:
text
这个因子的质因子总个数正好是 s
也就是:
text
第 m + s - 1 层:所有 Ω(x) = s 的因子 x
条件 1 为什么满足
如果 y 是 x 的真因子,那么:
text
Ω(y) < Ω(x)
所以 y 一定在更早的层。
条件 2 为什么满足
同一层里所有数的质因子总个数都是同一个 s,并且 s >= 2。
可以把一个因子看成一个"质因子袋子"。
例如:
text
12 = 2 * 2 * 3
就是袋子:
text
[2, 2, 3]
同一层的袋子大小都相同。我们可以按一种类似 Gray Code 的顺序排列这些袋子:每次只替换一个质因子,保留其它 s - 1 个质因子。
因为 s >= 2,相邻两个袋子至少还共享一个质因子,所以对应两个数的 gcd > 1。
更直观地看:
text
2*2*5 -> 2*3*5
它们都含有 2 和 5,所以 gcd > 1。
因此同一层可以排成符合要求的链。
于是总层数正好是:
text
ω(n) + (Ω(n) - 1) = Ω(n) + ω(n) - 1
结合前面的下界,这就是最优答案。
例子
n = 120
分解:
text
120 = 2^3 * 3^1 * 5^1
所以:
text
Ω(120) = 3 + 1 + 1 = 5
ω(120) = 3
答案:
text
5 + 3 - 1 = 7
一种分层示意:
| 层 | 内容 |
|---|---|
| 1 | 5 |
| 2 | 3 |
| 3 | 2 |
| 4 | 质因子总个数为 2 的因子 |
| 5 | 质因子总个数为 3 的因子 |
| 6 | 质因子总个数为 4 的因子 |
| 7 | 120 |
n = 2^5 = 32
text
Ω(32) = 5
ω(32) = 1
答案 = 5 + 1 - 1 = 5
这和样例输出一致。
算法流程
- 预处理
1..10^6每个数的最小质因子; - 对每个测试用例分解
n; - 统计:
totalPrimeFactors = Ω(n);distinctPrimeFactors = ω(n);
- 输出:
text
totalPrimeFactors + distinctPrimeFactors - 1
易错点
Ω(n)要重复计算质因子,例如12 = 2^2 * 3,所以Ω(12) = 3。ω(n)只计算不同质因子,例如12的不同质因子是2, 3,所以ω(12) = 2。- 答案不是约数个数,也不是最长整除链长度。最长整除链只给出
Ω(n),还要额外考虑不同质数不能在同一层。 n最大是10^6,直接试除也可以,但预处理最小质因子更稳。
复杂度分析
预处理:
text
时间复杂度:O(MAX log log MAX) 近似
空间复杂度:O(MAX)
每个测试用例分解:
text
时间复杂度:O(log n)
空间复杂度:O(1)
其中 MAX = 10^6。
C++ 代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
const int MAX_N = 1'000'000;
vector<int> spf(MAX_N + 1, 0); // smallest prime factor,最小质因子
for (int i = 2; i <= MAX_N; ++i) {
if (spf[i] == 0) {
for (long long j = i; j <= MAX_N; j += i) {
if (spf[j] == 0) {
spf[j] = i;
}
}
}
}
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
int totalPrimeFactors = 0; // Ω(n):质因子总个数,重复计算
int distinctPrimeFactors = 0; // ω(n):不同质因子个数
int x = n;
while (x > 1) {
int p = spf[x];
++distinctPrimeFactors;
while (x % p == 0) {
x /= p;
++totalPrimeFactors;
}
}
cout << totalPrimeFactors + distinctPrimeFactors - 1 << '\n';
}
return 0;
}