CF895C dp/线性基

来源:互联网 发布:知世风涧澈 编辑:程序博客网 时间:2024/06/07 19:48

链接:点击打开链接

题意: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;}



原创粉丝点击