题意
给定序列。
每个数不超过7个除数
求最小子序列使得乘积是完全平方数
题解
不超过7个除数,等价于最多两个质因数。
并且我们只关心质因数的奇次方。
所以我们除去所有的平方项,留下奇次方。
只要保证这个数的奇次方有两个,即是平方项了。
我们考虑,对于一个数建一条边,边上连两个质因数。
如果是质数,就连1和自己。
不是质数就不需要,因为对答案没有贡献
比如
10
=
2
∗
5
10=2*5
10=2∗5,如果连接1和10,那么要想有两个比如说
10
∗
2
10*2
10∗2,那么必然也有质因数
2
2
2和
5
5
5.
所以直接用
2
2
2和
5
5
5即可。
找最小环,这样对于上面的所有质因数都有两个数拥有,那么就是都是偶次方了。
找最小环用
b
f
s
bfs
bfs,枚举点去
b
f
s
bfs
bfs,因为你不知道哪个点在最小环上。
不过能保证的是,最小环上总有一个点小于
1
e
3
1e3
1e3,否则就存在有个数
>
1
e
6
>1e6
>1e6了 。
枚举
1
e
3
1e3
1e3进行
b
f
s
bfs
bfs。
b
f
s
bfs
bfs判环就是记录距离,如果到下一个点的这条边没访问过,且下一个点已经被访问过,那么就是环。
对边标号进行
v
i
s
vis
vis记录即可,直接令其等于当前源头,这样能避免清空的复杂度。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn = 1e6+50;
typedef long long ll;
int cnt=0;
vector<pair<int,int> >G[maxn];
int vis[maxn],len[maxn],ans=0x3f3f3f3f;
void bfs(int i){
queue<int>q;
q.push(i);
memset(len,0x3f,sizeof(len));
len[i]=0;
while(!q.empty()){
int u=q.front();q.pop();
// cout<<u<<":"<<" ";
for(auto it:G[u]){
int v=it.first,id=it.second;
if(vis[id]==i)continue;
// cout<<v<<" ";
vis[id]=i;
if(len[v]!=0x3f3f3f3f)ans=min(ans,len[u]+len[v]+1);
else len[v]=len[u]+1,q.push(v);
}
// puts("");
}
}
int main(){
int n,x;cin>>n;
FOR(i,1,n){
scanf("%d",&x);
for(int j=2;j<=sqrt(x);j++){
while(x%(j*j)==0)x/=j*j;
}
if(x==1){
puts("1");
return 0;
}
vector<int>tmp;
for(int j=2;j<=sqrt(x);j++){
if(x%j==0){
tmp.push_back(j);
while(x%j==0)x/=j;
}
}
if(x!=1)tmp.push_back(x);
if(tmp.size()==1){
G[1].push_back(make_pair(x,++cnt));
G[x].push_back(make_pair(1,cnt));
}
else{
G[tmp[0]].push_back(make_pair(tmp[1],++cnt));
G[tmp[1]].push_back(make_pair(tmp[0],cnt));
}
}
FOR(i,1,sqrt(maxn-1))bfs(i);
if(ans==0x3f3f3f3f)puts("-1");
else cout<<ans<<endl;
}
其实这题我一开始也不会看的是题解,但是网上很多人用除数建边,我觉得常数有点大,用质因数的话就能少 7 7 7倍的边。最后跑了 1300 m s 1300ms 1300ms。