CF1325E - Ehab's REAL Number Theory Problem(最小环)
题目
有\(n\)个数,每个数至多只有7个因子。问从中最少取多少个数相乘结果为完全平方数。
题解
完全平方数就是质因子数幂次都为偶数。对于每个数\(p_1^{c_1}...p_k^{c_k}\),可以把幂次对2取模,这样就可以写成\(p_1...p_k\)。由于至多只有7个因子,所以每个数至多只有2个质因数。如果没有质因数,那么说明找到一个完全平方数,答案为1。否则设这两个质因数为\(p,q\)(如果只有1质因子个那么就令另一个为1)。对于每个数都有一对\((p,q)\),把这个数对看作一个无向边。这些边构成的图中,每个环就对应这完全平方数的取法!所以问题转换为求无向图的最小环。
如果有重边,那么最小环长度为2;否则对于边权为1的图来说,求最小环的复杂度为\(O(n^2)\)。因为要对每个点都bfs一次计算环。然而,事实上,对于某个点进行bfs,可以遍历到所有包含该点的环。之所以要对每个点都bfs,是因为在任意图中,不能确定所有环都被遍历到了,所以才要对每个点都bfs。
但是对于在这题的限制下,两个相邻结点的值不会超过\(\sqrt{\max(a_i)}\),否则边对应的数就超过\(max(a_i)\)。这说明每个环都至少包含一个结点值小于\(\sqrt{\max(a_i)}\)。所以只需对小于\(\sqrt{\max(a_i)}\)的结点bfs就可以确保每个环都考虑到了。
时间复杂度\(O(n\sqrt{M})\),\(M\)为\(n\)个数的最大值。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const double eps = 1e-5;
typedef pair<int, int> PII;
bool isnp[N];
vector<int> d[N];
vector<PII> ed;
int vis[N], tag;
int len[N],fa[N];
vector<int> np[N];
int bfs(int p) {
int res = INF;
queue<int> q;
q.push(p);
fa[p] = 0;
vis[p] = tag;
len[p] = 0;
while(!q.empty()) {
int cur = q.front();
q.pop();
for(int nt : np[cur]) {
if(nt == fa[cur]) continue;
if(vis[nt] == tag) {
res = min(res, len[nt] + len[cur] + 1);
} else {
len[nt] = len[cur] + 1;
vis[nt] = tag;
fa[nt] = cur;
q.push(nt);
}
}
}
return res;
}
int main() {
IOS;
isnp[1] = 1;
for(int i = 2; i < N; i++) {
if(!isnp[i]) {
d[i].push_back(i);
for(int j = 2 * i; j < N; j += i) {
d[j].push_back(i);
isnp[j] = 1;
}
}
}
int ans = INF;
int n;
int mx = 0;
cin >> n;
for(int i = 1; i <= n; i++) {
int x;
cin >> x;
mx = max(x, mx);
bool flag = 1;
vector<int> tmp;
for(int di : d[x]) {
int tx = x, cnt = 0;
while(tx % di == 0) {
tx /= di;
cnt++;
}
if(cnt % 2) tmp.push_back(di);
}
if(tmp.empty()) {
ans = 1;
} else if(tmp.size() == 1) {
ed.push_back({1, tmp[0]});
} else {
ed.push_back({tmp[0], tmp[1]});
}
}
if(ans == INF) {
sort(ed.begin(), ed.end());
int m = unique(ed.begin(), ed.end()) - ed.begin();
if(m == ed.size()) {
for(int i = 0; i < m; i++) {
int u = ed[i].first, v = ed[i].second;
np[u].push_back(v);
np[v].push_back(u);
}
for(int i = 1; i * i <= mx; i++) {
tag++;
ans = min(ans, bfs(i));
}
} else {
ans = 2;
}
}
if(ans == INF) cout << -1 << endl;
else cout << ans << endl;
}