题意就是说两个数如果最大公约数大于一,就可以连一条边,问你有几个连通块,应该是个裸地并查集,但是数据量有点大,不能暴力。
我真是服了我自己了,比赛的时候交了7遍没交过,赛后一眼就看出bug在哪里了,挺难想的一个并查集,我们先与处理出来每个数所有的约数,然后我们对于输入的每个数去跑并查集,可以连到一起的归并到一个集合里去。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int maxn=1000000+5;
vector<int >vec[maxn];
int pre[maxn];
int a[maxn];
int flag[maxn];
void Init(int n){
for(int i=0;i<=n;i++){
pre[i]=i;
}
}
int Find(int x){
return x==pre[x]?x:pre[x]=Find(pre[x]);
}
void mix(int x,int y){
int xx=Find(x);
int yy=Find(y);
if(xx!=yy){
if(xx>yy)
pre[yy]=xx;
else
pre[xx]=yy;
}
}
int main(){
for(int i=2;i<maxn;i++){
for(int j=i*2;j<maxn;j+=i){
vec[j].push_back(i);
}
}
int cnt=0;
int t,n;
scanf("%d",&t);
while(t--){
cnt++;
scanf("%d",&n);
Init(maxn);
memset(flag,0,sizeof(flag));
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
for(int j=0;j<vec[a[i]].size();j++){
mix(a[i],vec[a[i]][j]);
}
}
printf("Case %d: ",cnt);
int ans=0;
for(int i=0;i<n;i++){
int root=Find(a[i]);
//cout<<root<<endl;
if(!flag[root]){
ans++;
if(a[i]!=1)
flag[root]=1;
}
}
printf("%d\n",ans);
}
return 0;
}