P9473 [yLOI2022] 西施江南
一扶苏一
·
·
题解
Analysis
考虑唯一分解定理,对 n 个数 a_1, a_2, a_3, \dots a_n,设 a_i = \prod p_j^{c_{i,j}}。则其 \gcd = \prod p_j ^{\min\limits_i c_{i,j}},\mathrm{lcm} = \prod p_j^{\max\limits_i c_{i,j}}。\gcd \times \mathrm{lcm} = \prod p_j^{\min\limits_i c_{i,j} + \max\limits_i c_{i,j}}。又 \prod a_i = \prod p_j^{\sum_{i= 1}^n c_{i,j}}。于是回答为是的充要条件是 \sum_{i = 1}^n c_{i,j} = \min c_{i,j} + \max c_{i,j}。易见 \max c_{i,j} 一定是左边的一项,所以需要 \min c_{i,j} 等于左边剩下的项。
讨论一下:
-
-
当 n > 2 时,假设 \min c_{i,j} = k。不妨假设 \min c_{ i,j} = c_{1,j} \leq c_{2, j} \leq c_{3, j} \leq \dots \leq c_{n,j} = \max c_{i,j}。想要让这些项目的和等于 \min c_{i,j} + \max c_{i,j},则 c_{2,j} = c_{3, j} = c_{4, j} = \dots = c_{n - 1,j} = 0。又 c_{2,j} \geq \min c_{i,j},于是 \min c_{i,j} = 0。
因此,此时每个质数只能作为一个数字的因子。换句话说,数字是两两互质的。
综上,当 n = 2 时,总是回答是,否则如果给定的数字两两互质,回答是,否则回答否。
子任务 1
## 子任务 2
$n \leq 5$ 时,可以暴力计算这些数字的积,检查他们的 gcd 和 lcm 的关系。
## 子任务 3
$n \leq 1000$ 时,可以暴力枚举每一对数,求他们的 gcd,看它们是不是两两互质。时间复杂度 $O(n^2 \log a)$。
## 子任务 4
回到『每个质数只能作为一个数的因子』这个表述上,可以据此检查所有数是否两两互质。方法是:对每个数 $O(\sqrt a_i)$ 地分解质因子。用一个数组记录每个质因子是否在之前的数中出现过。如果某个质因子之前出现过,直接返回否即可。时间复杂度 $O(n \sqrt a_i)$。
## 子任务 5
可以在线性筛的同时筛出每个数的最小质因子,具体的方法见 [B3716](https://www.luogu.com.cn/problem/B3716)。
这样就可以 $O(\log a_i)$ 筛出每个数的质因子,然后按子任务 $4$ 的做法判断即可。时间复杂度 $O(T n \log a_i)$。
## Code
```cpp
#include <vector>
#include <iostream>
const int maxn = 100000005;
int T;
std::vector<int> prm, pre; // pre is the min-factor array.
bool np[maxn];
bool oc[maxn];
void getPrime(const int N = 100000000) {
pre.resize(N + 1);
for (int i = 2; i <= N; ++i) {
if (!np[i]) {
prm.push_back(i); pre[i] = i;
}
for (auto p : prm) if (i * p <= N) {
int k = i * p;
np[k] = true;
pre[k] = p;
if (i % p == 0) break;
} else break;
}
}
int main() {
getPrime();
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
for (std::cin >> T; T; --T) {
int n;
for (auto i : prm) oc[i] = false;
std::cin >> n;
std::vector<int> a(n);
for (auto &i : a) std::cin >> i;
if (n == 2) {
std::cout << "Yes\n";
} else {
bool ans = true;
for (auto i : a) {
std::vector<int> tmp;
while (i != 1) {
tmp.push_back(pre[i]);
i /= pre[i];
}
for (auto j : tmp) if (oc[j]) {
ans = false;
}
for (auto j : tmp) {
oc[j] = true;
}
}
std::cout << (ans ? "Yes" : "No") << std::endl;
}
}
}
```