Codeforces Round #448 (Div. 2) C. Square Subsets 状压DP

平方数肯定满足所有质数的次幂都是偶数,具体数值我们不关心只关心奇偶。

由于a[i]的取值是1-70,我们发现1-70中质数有19个  

于是我们可以用  (1<<19)-1  个状态表示这19个质数的幂次是奇数还是偶数

由于所有集合都是由空集扩展而来。

我们设dp的初状态dp[0][0]为 没有处理数时,所有质数次幂为偶数时  的个数  是1.

由于数加入顺序对组成最终集合的个数没有影响:

我们枚举i从1到70  dp[i][j]表示处理到i时,状态为j时,集合的个数。

转移时:

对数值i的个数cnt进行讨论(zt[i]为 i的状态 即:19个质数的次幂的状态压缩)

当cnt为奇数时:dp[i][j]=dp[i-1][j^zt[i]]*x

x=C(1,cnt)+C(3,cnt)+………… +C(cnt,cnt)=2^(cnt-1)

同理,当cnt为偶数时

dp[i][j]=dp[i-1][j]*x

x=C(2,cnt)+C(4,cnt)+………… +C(cnt,cnt)=2^(cnt-1)

转移式出来了,初状态dp[0][0]=1表示空集.

末状态dp[n][0]表示所有子集中  满足所有质数的幂次都是偶数的个数  即题意要求的。

减去1  空集 即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
const int mod =1e9+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
ll dp[2][(1<<19)+7];
ll a[M],b[M];
int prime[100],id[100],sz,zt[72];
int p[30],c[30];
ll h[M];
ll qpow(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b/=2;
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	ll n;
  	cin>>n;
  	for(int i=1;i<=n;i++)cin>>a[i],b[a[i]]++;
  	for(int i=2;i<=70;i++)
  	{
  		bool f=true;
  		for(int j=2;j<=sqrt(i);j++)
  		if(i%j==0)f=false;
  		if(f)prime[++sz]=i,id[i]=sz;
	}
	h[0]=1;
	for(int i=1;i<=n;i++)h[i]=h[i-1]*2%mod;
	zt[1]=0;
	for(int i=2;i<=70;i++)
	{
		ll ans=0;
		int tp=i;
		for(int j=2;j<=tp;j++)
		{
			if(tp%j==0)
			{
				int z=0;
				while(tp%j==0)tp/=j,z++;
				z=z%2;
				ans+=(1<<(id[j]-1))*z;
			}
		}
		zt[i]=ans;
	}
//	cout<<"ok"<<endl;
	dp[0&1][0]=1;
	for(int i=1;i<=70;i++)
	{
		int tp=zt[i];
		if(b[i]==0)
		{
			for(int j=0;j<(1<<sz);j++)dp[i&1][j]=dp[(i-1)&1][j];
			continue;
		}
		for(int j=0;j<(1<<sz);j++)
		{
			dp[i&1][j]=dp[(i-1)&1][j^tp]*h[b[i]-1]%mod;
			dp[i&1][j]=(dp[i&1][j]+dp[(i-1)&1][j]*h[b[i]-1])%mod;
		}
	//	cout<<i<<"  "<<dp[i][0]<<"  "<<dp[i][1]<<"  "<<dp[i][4]<<" "<<dp[i][5]<<endl;
	}
	ll ans=(dp[70&1][0]-1+mod)%mod;//有一种空集的情况 
	cout<<ans<<endl;
	return 0;
}
发布了284 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/bjfu170203101/article/details/104269950