题解[CF895C Square Subsets]

题目描述

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

\(1\leq a_i\leq 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\n",f[now][0]-1);
	return 0;
}

完结撒花❀

posted @ 2021-09-02 21:32  xxbbkk  阅读(63)  评论(0编辑  收藏  举报