1. 程式人生 > >NOIP2018模擬賽 (並查集+線性篩)2018 10 23 T1 x

NOIP2018模擬賽 (並查集+線性篩)2018 10 23 T1 x

簡述題意:

給定一個長度為 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^{n}-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 ;
}