版权声明:本文为博主原创文章,转载标明出处。 https://blog.csdn.net/xlzhang223/article/details/78736846
链接:点击打开链接
题意:n个数,选子集乘积之和是完全平方数的子集个数
思路:首先题目中ai非常小,显然可以状态压缩因子把问题转化成异或和为0的方案数字。
在此基础上可以DP解决或者使用线性基
DP方法
用dp[i][j]表示i次插入时异或和为j的方案数,我们可以想到插入一个新数字就是 dp[i-1][j ^ st]+=dp[i-1][j]
但是到此每插入一个数都要遍历j (2^19) ,所以我们考虑用桶压缩输入,预处理出来n个数中选奇数个或者选偶数个元素的方案数。组合计数一下便可以解决问题。
具体看代码。
DP
#include <bits/stdc++.h>
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define pii pair<int,int>
#define MEM(x) memset(x,0,sizeof(x))
using namespace std;
int n,m,x;
const int p=1e9+7,maxn=1e6+10;
int idx[100];
vector<int> Pr;
bool isprime(int x){
for(int i=2;i<x;i++)
if(x%i==0) return false;
return true;
}
int getst(int x){
if(x==1) return 0;
int st=0;
for(int i=0;i<Pr.size();i++)
while(x%Pr[i]==0) st^=(1<<i),x/=Pr[i];
return st;
}
long long C1[maxn],C2[maxn];
long long dp[maxn],last[maxn];
int main(){
MEM(C1);MEM(C2);MEM(idx);MEM(dp);
for(int i=2;i<=70;i++)if(isprime(i)) Pr.PB(i);
C1[1]=1;
for(int i=2;i<maxn;i++){
C1[i]=(C1[i-1]+C2[i-1]+1)%p;
C2[i]=(C2[i-1]+C1[i-1])%p;
}
scanf("%d",&n);
int cnt=0;
dp[0]=last[0]=1;
for(int i=0;i<n;i++) scanf("%d",&x),idx[x]++;
for(int i=1;i<=70;i++){
if(idx[i]==0) continue;
int st=getst(i);
for(int j=0;j<maxn;j++){
dp[j^st]=(dp[j^st]+last[j]*C1[idx[i]])%p;
dp[j]=(dp[j]+last[j]*C2[idx[i]])%p;
}
for(int j=0;j<maxn;j++) last[j]=dp[j];
}
printf("%lld\n",(dp[0]-1+p)%p);
return 0;
}
线性基方法:
首先线性基的概念参考点击打开链接(2017西安赛区考了这个),根据性质分析这个问题
X插入失败 ----> 存在可以构成X的子集
已知和为0的集合 ----> 若插入失败
设有子集{S1},{S2},{S3}没有交集
则一定存在{S1}^{S2}=0 {S2}^{S3}=x
则可以替换S2为{S3}^x 答案*=2
(其中S2、S3可以是空集)
综上所述,线性基插入是失败时,和为0的子集数量*=2
代码:
#include <bits/stdc++.h>
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define pii pair<int,int>
#define MEM(x) memset(x,0,sizeof(x))
using namespace std;
int n,m,x;
const int p=1e9+7;
struct L_B{
long long d[61];
L_B(){memset(d,0,sizeof(d));}
bool insert(long long val){
for (int i=60;i>=0;i--)
if (val&(1LL<<i)){
if (!d[i]){
d[i]=val;
break;
}
val^=d[i];
}
return val>0;
}
};
vector<int> Pr;
bool isprime(int x){
for(int i=2;i<x;i++)
if(x%i==0) return false;
return true;
}
int getst(int x){
int st=0;
for(int i=0;i<Pr.size();i++)
while(x%Pr[i]==0) st^=(1<<i),x/=Pr[i];
return st;
}
int main(){
for(int i=2;i<=70;i++)if(isprime(i)) Pr.PB(i);
scanf("%d",&n);
L_B B;
int cnt=0;
long long ans=1;
for(int i=0;i<n;i++){
scanf("%d",&x);
if(!B.insert(getst(x))) ans=(ans*2)%p;
}
printf("%lld\n",(ans-1+p)%p);
return 0;
}