【一句話題意】給定一個包含N 個非負整數的集合A,請將A 分成兩個子集P、Q,且使得。請計算這樣的劃分方法總數mod1000000007 後的值。 n<=1e6
【分析】一道可做的數論題。按質因數劃分,P和Q中不能有相同的質因數,擁有相同質因數的數必須放在同一個集合。將每個數分解質因數後,將每個數和質因數之間連一個雙向邊,顯然的是每個連通塊都必須放在同一個集合。考慮方案時,從1到n-1枚舉放入集合P的連通塊的組合方案數就是。
這裏有個小技巧:(每個數都有選或不選兩種情況)
所以方案數就是2n-2
【code】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
using namespace std;
const int maxn=1e5+100;
const int N=1e6+100;
const int mod=1e9+7;
int n,a[maxn];
int fac[N],prime[N],cnt;
struct Prime{
int id,nxt;
}p1[maxn*18];
int h1[N],u1[N],tot1;
struct node{
int id,nxt;
}p2[maxn*18];
int h2[maxn],u2[maxn],tot2;
inline void read(int &x){
x=0;char tmp=getchar();
while(tmp<'0'||tmp>'9') tmp=getchar();
while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
void prework(){
for(int i=2;i<=1e6;i++){
if(!fac[i]) fac[i]=++cnt,prime[cnt]=i;
for(int j=1;i*prime[j]<=1e6&&j<=cnt;j++){
fac[i*prime[j]]=j;
if(j==fac[i]) break;
}
}
}
inline void add_p1(int x,int y){
p1[tot1].id=y;
p1[tot1].nxt=h1[x];
h1[x]=tot1++;
}
inline void add_p2(int x,int y){
p2[tot2].id=y;
p2[tot2].nxt=h2[x];
h2[x]=tot2++;
}
void dfs(int x,int d){
if(d==1){
u1[x]=1;
for(int i=h1[x];i!=-1;i=p1[i].nxt){
if(!u2[p1[i].id]) dfs(p1[i].id,2);
}
}
else if(d==2){
u2[x]=1;
for(int i=h2[x];i!=-1;i=p2[i].nxt){
if(!u1[p2[i].id]) dfs(p2[i].id,1);
}
}
}
inline int pw(rint x,rint p){
rint ret=1;
while(p>0){
if(p&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod,p>>=1;
}
return ret;
}
int main(){
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
prework();
int T;cin>>T;
while(T--){
cin>>n;
for(rint i=1;i<=n;i++)
read(a[i]);
memset(h1,-1,sizeof(h1));
memset(h2,-1,sizeof(h2));
memset(u1,0,sizeof(u1));
memset(u2,0,sizeof(u2));
tot1=tot2=0;
for(rint i=1;i<=n;i++){//nlogn
int x=a[i];
while(x>1){
int d=fac[x];
add_p1(d,i);
add_p2(i,d);
while(fac[x]==d){
x/=prime[d];
}
}
}
int ans=0;
for(rint i=1;i<=n;i++){
if(!u2[i]){
ans++;
dfs(i,2);
}
}
cout<<(pw(2,ans)-2+mod)%mod<<endl;
}
return 0;
}