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;}
阅读全文
0 0
- CF895C dp/线性基
- CF895C:C. Square Subsets(状压dp)
- 线性DP
- 线性dp
- 线性dp
- 线性dp
- DP专辑之线性DP
- HDU 1003 (线性dp)
- 【线性DP】基础练习
- 几何 +线性dp
- 线性DP WA
- 花瓶的线性dp
- 线性dp<一>
- Uva 11584 线性DP
- Uva1625 线性DP
- Uva10003 线性DP
- Uva1626 线性DP
- PID217 线性DP
- SpringBoot小白教程 (一): 使用Maven新建SpringBoot工程
- QML Image动态刷新图片来自C++的QImage的一个巨坑
- Unable to complete the scan for annotations for web application [] due to a StackOverflowError
- samba共享存储服务
- Kotlin学习(一)
- CF895C dp/线性基
- Request和Response方法总结 - CSDN博客
- C++注释转化为C注释
- 【收藏】比较全的关于js获取css样式各种方法区别
- 240. Search a 2D Matrix II
- Spring Boot学习笔记----mybatis注解(一)
- java通过jdbc插入中文到mysql显示异常(问号或者乱码)
- The art of computer programming chapter1
- iptables