CF1325E Ehab's REAL Number Theory Problem
题目大意
给定 \(n\) 个数,每个数的因数个数不超过 \(7\),求最少选出多少个数能使得乘积为一个完全平方数。
无解输出 \(-1\)。
思路
约数个数定理:对于
\[n=\prod^{k}_{i=1}p_i^{a_i} \]\(n\) 的正约数个数为 \(\prod^{k}\limits_{i=1}(a_i + 1)\)。
那么可以转化题目中的条件,含有 \(3\) 个本质不同质因数的数含有 \(8\) 个因子,那么因数个数不超过 \(7\) 可以转化为每个数至多有 \(2\) 个本质不同的质因子。
容易发现,如果将每个数自身的平方因子都去掉,不会影响最终的答案,我们只需要考虑出现次数不为偶数的因数。
对于一个数 \(n\),可以表示为 \(p^x \cdot q^y\)(不保证 \(p,q \neq 1\))。将平方因子除尽后,显然有 \(x,y \in \left\{ 0,1 \right\}\),我们把 \(1\) 也看作一个质因数,对于每个已经除尽平方因子的数 \(n'\),我们在 \(p,q\) 间连边,每一条边表示原序列中的一个数。
可以发现,如果我们能找到一个子图,满足每个点的度数均为偶数,这样的子图的边数就是我们寻找的答案。
环显然满足这种性质,我们想使得答案最小,所以寻找最小环的大小。
一般的求最小环方法是 \(O(n ^ 3)\) 的 Floyd,显然是不行的。
考虑枚举起点 BFS 求最小环,但这样复杂度也是 \(O(n ^ 2)\) 的,考虑优化。
记 \(V = \max\left\{a_i\right\}\),我们发现,值大于 \(\sqrt{V}\) 的质数是不会与其他结点连边的,无需将其当作起点枚举。
时间复杂度 \(O(n \sqrt{V})\)。
Code
Code
// CF1325E
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2005000;
const int M = 2000000;
const int INF = 0x3f3f3f;
int n;
struct Edge{
int next,to;
}e[N << 1];
int cnt,h[N];
void Add(int u,int v) {
cnt ++;
e[cnt].next = h[u];
h[u] = cnt;
e[cnt].to = v;
}
bool p[N];
int tot,pri[N >> 2];
int pos[N],lim = 0;
void Seive() {
p[1] = 1;
pos[1] = 1;
for (int i = 2;i <= M; i++) {
if(!p[i]) {
tot ++;
pri[tot] = i;
pos[i] = tot + 1;
if(i < 1000)
lim ++;
}
for (int j = 1;j <= tot && i * pri[j] <= M; j++) {
p[i * pri[j]] = 1;
if(i % pri[j] == 0)
break;
}
}
return ;
}
void Divide(int x) {
int p = 0,q = 0,limit = sqrt(x);
for (int i = 2;i <= limit; i++) {
if(x % i == 0) {
int cnt = 0;
while(x % i == 0) {
cnt ++;
x /= i;
}
if(cnt & 1) {
if(p == 0)
p = i;
else
q = i;
}
}
}
if(x != 1) {
if(p == 0)
p = x;
else
q = x;
}
if(p == 0 && q == 0) {
cout << 1 << endl;
exit(0);
}
if(q == 0) {
Add(pos[p],1);
Add(1,pos[p]);
}
else {
Add(pos[p],pos[q]);
Add(pos[q],pos[p]);
}
return ;
}
int dis[N],ans = LONG_LONG_MAX;
void bfs(int s) {
queue<pair<int,int>> que;
memset(dis,0x3f3f3f,sizeof(dis));
dis[s] = 0;
que.emplace(s,0);
do {
int x = que.front().first,fa = que.front().second;
que.pop();
for (int i = h[x];i; i = e[i].next) {
int to = e[i].to;
if(to == fa)
continue;
if(dis[to] >= INF) {
dis[to] = dis[x] + 1;
que.emplace(to,x);
}
else if(dis[x] + dis[to] > 0)
ans = min(ans,dis[to] + dis[x] + 1);
}
}while(!que.empty());
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
Seive();
cin >> n;
for (int i = 1,x;i <= n; i++) {
cin >> x;
Divide(x);
}
for (int i = 1;i <= lim; i++)
bfs(i);
if(ans > n)
cout << "-1" << endl;
else
cout << ans << endl;
return 0;
}