题目链接:https://codeforces.com/contest/895/problem/C
题目大意:给你n个数。问有多少个子集的乘积为平方数。mod (1e9+7)。
思路一:
如果一个数的质因数都为偶数个,那么这个数就是平方数。
因为a[i]<=70。我们用状压来表示每个数的质因数的奇偶状态。奇为1.偶为0。
我们用dp[i][s]表示前i个数组成乘积为状态s的方案数。
然后我们枚举i-1的状态s转移。
如果i选择偶数个,那么i自己的集合为0, 那么i-1的状态必须为s。
如果i选择奇数个,那么i自己的集合为s0^s, 那么i-1的状态为s。
然后用滚动数组。
思路二:
法一:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[100005];
LL cut[75];
const LL p[]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};//19
LL c2[100005]={1};
LL f[2][1<<21];
const int mod = 1e9+7;
int main()
{
int n;
scanf("%d", &n);
for(LL i=1; i<=n; i++){
scanf("%lld", &a[i]);
cut[a[i]]++;
}
for(LL i=1; i<=n; i++){
c2[i]=c2[i-1]*2%mod;
}
f[0][0]=1;//初始化
int I=0;
for(LL i=1; i<=70; i++){
if(cut[i]==0){
continue;
}
I^=1;
int s0=0;//i本身的状态
int SI=i;
for(int k=0; k<19; k++){
while(SI%p[k]==0){
s0^=(1<<k);
SI/=p[k];
}
}
memset(f[I], 0, sizeof(f[I]));
for(LL s=0; s<(1<<19); s++){//枚举i-1的状态s
f[I][s]+=f[I^1][s]*c2[cut[i]-1]%mod;//如果i选择偶数个新状态为0^s
f[I][s^s0]+=f[I^1][s]*c2[cut[i]-1]%mod;//如果i选择奇数个新状态为s^s0
}
}
printf("%lld\n", (f[I][0]-1+mod)%mod);//f[0][0]为空集。不满足-1
return 0;
}
发二:
#include<bits/stdc++.h>
#define reg register
#define LL long long
using namespace std;
typedef long long ll;
const int MN=60;
ll a[61],tmp[61];
const LL p[]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};//19
LL c2[100005]={1};
const int mod = 1e9+7;
bool flag;
void ins(ll x){//构造
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i]){a[i]=x;return;}
else x^=a[i];
flag=true;
}
bool check(ll x){//是否能够插入 判断是否能够异或出一个数
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i])return false;
else x^=a[i];
return true;
}
ll qmax(ll res=0){//最大值
for(reg int i=MN;~i;i--)
res=max(res,res^a[i]);
return res;
}
ll qmin(){//最小值
if(flag)return 0;
for(reg int i=0;i<=MN;i++)
if(a[i])return a[i];
}
ll query(ll k){//第k小
reg ll res=0;reg int cnt=0;
k-=flag;if(!k)return 0;
for(reg int i=0;i<=MN;i++){
for(int j=i-1;~j;j--)
if(a[i]&(1ll<<j))a[i]^=a[j];
if(a[i])tmp[cnt++]=a[i];
}
if(k>=(1ll<<cnt))return -1;
for(reg int i=0;i<cnt;i++)
if(k&(1ll<<i))res^=tmp[i];
return res;
}
int getnum(int x){
int ans=0;
for(int k=0; k<19; k++){
while(x%p[k]==0){
ans^=(1<<k);
x/=p[k];
}
}
return ans;
}
int main(){
int n, x;scanf("%d",&n);
for(LL i=1; i<=n; i++){
c2[i]=c2[i-1]*2%mod;
}
for(int i=1; i<=n; i++){
scanf("%d", &x);
ins(getnum(x));
}
for(reg int i=MN;~i;i--){
if(a[i]){
n--;
}
}
printf("%lld\n",c2[n]-1);
return 0;
}