• 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 <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
    ",(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
    ",(ans-1+p)%p);
        return 0;
    }
    




  • 相关阅读:
    Ecshop文件结构(Ecshop二次开发辅助文档)
    Python的Web开发环境搭建
    我的第一个.Net网站
    Windows2008R2安装sqlserver2005远程登陆失败错误18456
    SQLSERVER批量更新根据主键ID字符串
    2009年9月国内与国外浏览器市场粗略对比
    SQL2000与SQL2005下高效分页语句
    开源IIS Rewrite组件IonicIsapiRewriter2.0Releasebin
    C++中的typedef的用法
    关于NP complete
  • 原文地址:https://www.cnblogs.com/zhangxianlong/p/10672477.html
Copyright © 2020-2023  润新知