题解[CF895C Square Subsets]

题目描述

给定数组(A) ,选取(A)的非空子集是他们的乘积为完全平方数。

(1leq a_ileq 70)

Sol

70以内的质数只有19个,这提示我们可以状压。

(f[i][S])为考虑了前(i)种数值,且质因子的状态为(S)

状态(S):若第(j)个质因子被使用的次数是奇数次,那(S)在二进制下的第(j)位是一。

转移时对数值进行质因数分解,设质因数分解后得到状态(T),这个数值的数量为(cnt)

考虑转移:

若使用奇数个此数值:

[f[i][S|T]=f[i-1][S]cdot2^{cnt-1} ]

若使用偶数个此数值:

[f[i][S]=f[i-1][S]cdot2^{cnt-1} ]

为什么是乘上(2^{cnt-1})呢?

因为选取此值共有(2^{cnt})种方案,其中选奇数个和选偶数个的情况各占一半。

Code

#include<bits/stdc++.h>
#define N (100010)
#define ll long long
using namespace std;
const int p[21]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
const ll P=1000000007;
int n,cnt[71];
ll pw[N],f[2][1000000];
inline int read(){
	int w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
int main(){
	n=read(),pw[0]=1,f[0][0]=1;
	for(int i=1;i<=n;i++) ++cnt[read()],pw[i]=pw[i-1]*2%P;
	int now=0;
	for(int i=1;i<=70;i++){
		if(!cnt[i]) continue;
		now^=1;
		memset(f[now],0,sizeof(f[now]));
		for(int S=0;S<(1<<19);S++){
			int T=S,num=i;
			for(int j=1;j<=19&&num>=p[j];j++)
				while(num%p[j]==0) num/=p[j],T^=(1<<(j-1));
			f[now][T]=(f[now][S]+1ll*pw[cnt[i]-1]*f[now^1][S]%P)%P;
			f[now][S]=(f[now][S]+1ll*pw[cnt[i]-1]*f[now^1][S]%P)%P;
		}
	}
	printf("%d
",f[now][0]-1);
	return 0;
}

完结撒花❀

原文地址:https://www.cnblogs.com/xxbbkk/p/15220895.html