题目描述
给定数组(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;
}