题目
题意:
给定一个序列,这个序列的因子数不超过7个,要求从这个序列里拿出尽可能少的数,使得他们的乘积为完全平方数。无解输出-1。
1
≤
n
≤
1
e
5
,
1
≤
a
[
i
]
≤
1
e
6
1≤n≤1e5,1≤a[i]≤1e6
1≤n≤1e5,1≤a[i]≤1e6
分析:
首先完全平方数有一个性质,就是素因子分解后,每个素因子的指数都是偶数,所以对于一个数它的素因子是偶数的就不用考虑了。其次我们考虑每个数字的因子数不超过7个,那么可以发现素因子个数不超过2个。那么选择一个数就变成了选择两个素因子(只有一个素因子时加上1就好),给素因子之间连边。然后我们需要素因子都出现两次,也就是选择的素因子的度都为2。那就变成了求无权图的最小环,bfs即可。
可以发现素因子的个数很多,但因为每个元素都在1e6以内,所以图上那些大于1000的点,一定连着小于1000的数,所以我们只需要枚举1000以内的素数即可。
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 1e6+5;
vector<int> g[maxn];
vector<int> num;
int pre[maxn],dis[maxn],vis[maxn];
void divide(int x)
{
num.clear();
int a = x;
for (int i = 2; i * i <= x; i++)
{
if( a % i == 0 )
{
int cnt = 0;
while( a % i == 0 )
{
a /= i;
cnt ^= 1;
}
if( cnt ) num.push_back(i);
}
}
if( a > 1 ) num.push_back(a);
}
int bfs(int begin)
{
vector<int> use;
queue<int> q;
q.push(begin);
dis[begin] = 0,pre[begin] = 0;
use.push_back(begin);
int ans = 1e9;
while( !q.empty() )
{
int x = q.front();
q.pop();
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( t == pre[x] ) continue;
if( dis[t] != -1 )
{
ans = dis[x] + dis[t] + 1;
break;
}
dis[t] = dis[x] + 1;
pre[t] = x;
q.push(t);
use.push_back(t);
}
if( ans != 1e9 ) break;
}
for (int i = 0; i < use.size(); i++)
{
dis[use[i]] = -1;
pre[use[i]] = 0;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;
cin >> n;
int ans = 1e9,tmp = 1e9;
memset(dis,-1,sizeof(dis));
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
if( vis[x] == 1 ) tmp = 2;
vis[x] = 1;
divide(x);
if( num.size() == 2 )
{
g[num[0]].push_back(num[1]);
g[num[1]].push_back(num[0]);
}else if( num.size() == 1 )
{
g[num[0]].push_back(1);
g[1].push_back(num[0]);
}else ans = 1;
}
ans = min(ans,tmp);
for (int i = 1; i <= 1000; i++)
{
ans = min(ans,bfs(i));
}
if( ans == 1e9 ) cout << -1 << '\n';
else cout << ans << '\n';
return 0;
}