题解[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;
}