CF895C dp/线性基

链接:点击打开链接

题意: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 
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define pii pair
#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 Pr;
bool isprime(int x){
    for(int i=2;i


线性基方法: 

 首先线性基的概念参考点击打开链接(2017西安赛区考了这个),根据性质分析这个问题

X插入失败     ----> 存在可以构成X的子集 

已知和为0的集合 ---->  若插入失败
                        设有子集{S1},{S2},{S3}没有交集 
                      则一定存在{S1}^{S2}=0 {S2}^{S3}=x 
                        则可以替换S2为{S3}^x 答案*=2
                        (其中S2、S3可以是空集) 
综上所述,线性基插入是失败时,和为0的子集数量*=2

代码:

#include 
#define PB push_back
#define MP make_pair
#define X first
#define Y second
#define pii pair
#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<0;
    }
};
vector Pr;
bool isprime(int x){
    for(int i=2;i



你可能感兴趣的:(dp,数学)