#并查集,线性筛#nssl 1470 X

题目


分析

显然答案就是\(2^{连通块个数}-2\),

将每个数的质数所在的集合合并,

最后判断连通块个数即可(线性筛少了个等号改了半天QWQ)


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=1e5+1,M=1e6,mod=1e9+7;
int two[N],prime[N],v[N*10],f[N],a[N],n,Cnt,ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
signed main(){
	for (rr int i=2;i<=M;++i){
		if (!v[i]) prime[++Cnt]=i,v[i]=Cnt;
		for (rr int j=1;j<=Cnt&&i<=M/prime[j];++j){
			v[i*prime[j]]=j;
			if (i%prime[j]==0) break;
		}
	}
	two[0]=1;
	for (rr int i=1;i<N;++i)
	    two[i]=mo(two[i-1],two[i-1]);
	for (rr int T=iut();T;--T){
		ans=n=iut();
		for (rr int i=0;i<=Cnt;++i) f[i]=i;
		for (rr int i=1;i<=n;++i){
			a[i]=iut();
			for (rr int j=a[i],last=0;j>1;){
				rr int now=v[j];
				if (last){
					rr int fa=getf(last),fb=getf(now);
					if (fa>fb) fa^=fb,fb^=fa,fa^=fb;
					if (fa!=fb) f[fa]=fb;
				}
				while (j%prime[now]==0) j/=prime[now];
				last=now;
			}
		}
		for (rr int i=1;i<=n;++i)
		if (a[i]>1){
			rr int F=getf(v[a[i]]);
			if (!F) --ans; else f[F]=0;
		}
		print(mo(two[ans],mod-2)),putchar(10);
	}
	return 0;
} 
posted @ 2020-08-13 21:19  lemondinosaur  阅读(64)  评论(0编辑  收藏  举报