NOIP2018模擬賽 (並查集+線性篩)2018 10 23 T1 x
阿新 • • 發佈:2018-12-17
簡述題意:
給定一個長度為 n 的正整數序列 ai。 將 1,2,...,n 劃分成兩個非空集合 S、T,使得 gcd(∏i∈Sai,∏i∈Tai)=1。 求劃分方案數,對 109+7取模。
Input 3 3 2 3 1 3 2 3 6 4 2 3 6 1 Output 6 0 2 2 Explanation • 第1 組資料,任意一種非空集合劃分均滿足。 • 第2 組資料,任意一種非空集合劃分均不滿足。 • 第3 組資料,S = {1; 2; 3}; T = {4},gcd(a1 a2 a3; a4) = 1,或者S = {4}; T = {1; 2; 3}, gcd(a4; a1 a2 a3) = 1。
題解:
對於兩個數 a,b,must處於同一個集合,當其含有相同因數。 並查集維護含有相同因子的數。 最後統計所有的 nn個數被分成了 x 個集合 答案就是-2,減2的原因是減去兩個空集合!
1需要特判(加到mi上)
時間複雜度:O(n*T)
注意,對於“1”的特判!!!
tp的值要加到mi上@!!!!!(~_~)!
程式碼如下:
#include <bits/stdc++.h> #define ll long long #define N 1000005 using namespace std; const ll mod=1000000007; int n; int a[N]; int appe[N]; int prime[N],vis[N],cnt; void fprime(int nn) { for(int i = 2;i <= nn;i++) { if(!vis[i]) { cnt++; prime[cnt]=i; } for(int j = 1;(j<=cnt)&&(i*prime[j]<=nn);j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) break; } } } ll PowerMod(ll x,ll y) { ll ret=1; while(y) { if(y%2) { ret=ret*x%mod; } y=y/2; x=x*x%mod; } return ret%mod; } int fa[N]; int findf(int x) { if(x==fa[x]) return x; return fa[x]=findf(fa[x]); } int viss[N]; int main() { freopen("x.in","r",stdin); freopen("x.out","w",stdout); //printf("%d\n",(sizeof(a)+sizeof(appe)+sizeof(prime)+sizeof(vis)+sizeof(fa)+sizeof(viss))/1024/1024); int T; scanf("%d",&T); fprime(1000000); while(T--) { memset(vis,0,sizeof(vis)); memset(viss,0,sizeof(viss)); memset(appe,0,sizeof(appe)); int maxn=-0x3f3f3f3f; int tp=0; scanf("%d",&n); for(int i = 1;i <= 1000000;i++) { fa[i]=i; } for(int i = 1;i <= n;i++) { scanf("%d",&a[i]); maxn=max(maxn,a[i]); appe[a[i]]=1; tp+=(a[i]==1); } for(int i = 1;i <= cnt;i++) { for(int j = 1;j*prime[i]<=maxn;j++) { if(appe[j*prime[i]]) { int t1=findf(prime[i]); int t2=findf(j*prime[i]); if(t1!=t2) { fa[t1]=t2; } } } } int mi=0; for(int i = 1;i <= n;i++) { int tt=findf(a[i]); if(!viss[tt]) { mi++; viss[tt]=1; } } ll ans=0; if(tp>1) mi+=tp-1;//對於1的特判,需要加到冪數中,我的40分啊啊啊啊啊 ans=PowerMod(2,mi); //剛剛加到了ans上! ans-=2; if(ans<0) ans+=mod; printf("%I64d\n",ans); } return 0 ; }