标签:style print 题目 状压dp turn \n com 子集 open
题目大意:
给出一个长度为n的序列,构造出一个序列使得它们的位与和为0,求方案数
也就是从序列里面选出一个非空子集使这些数按位与起来为0.
看了好久才明白题解在干嘛,我们先要表示出两两组合位与和为0的所有情况
先hx一下每个数出现的次数,然后我们从遍历 i ,i 是二进制的数位
然后遍历所有的情况,如果第 i 位有1,那么说明我们去掉第 i 位的1就是又一种情况!
其实我们统计的是所有数在删掉/不删掉每一位的1 所有可能出现的数!
那么,状态内任意组合,不能取空集,总数就是
再根据容斥原理(最玄学的一步),根据状态内1的数量进行容斥,即可得到答案
仍然不是很理解,明天再思考思考
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define ll long long 5 #define N (1<<20)+100 6 #define maxn 1000000 7 #define mod 1000000007 8 using namespace std; 9 10 int n; 11 int hx[N]; 12 ll xx,yy,tt; 13 ll pw[N],sum[N],ans[22]; 14 void get_pw() {pw[0]=1;for(ll i=1;i<=n+1;i++) pw[i]=(pw[i-1]*(ll)2)%mod;} 15 16 int main() 17 { 18 freopen("data.in","r",stdin); 19 scanf("%d",&n); 20 int x; 21 for(int i=1;i<=n;i++) 22 { 23 scanf("%d",&x); 24 hx[x]++; 25 sum[x]=hx[x]; 26 } 27 for(int j=0;j<20;j++) 28 { 29 for(int s=(1<<20)-1;s>=0;s--) 30 if(s&(1<<j)) sum[s^(1<<j)]+=sum[s]; 31 } 32 get_pw(); 33 for(int s=0;s<(1<<20);s++) 34 { 35 int cnt=0; 36 for(int j=0;j<20;j++) 37 if(s&(1<<j)) 38 cnt++; 39 ans[cnt]+=((pw[sum[s]]-1)%mod+mod)%mod; 40 ans[cnt]%=mod; 41 } 42 ll ret=0; 43 for(int i=0;i<20;i++) 44 { 45 if(i&1) ret-=ans[i],ret%=mod; 46 else ret+=ans[i],ret%=mod; 47 } 48 printf("%lld\n",(ret%mod+mod)%mod); 49 return 0; 50 }
CF449D Jzzhu and Numbers (状压DP+容斥)
标签:style print 题目 状压dp turn \n com 子集 open
原文地址:https://www.cnblogs.com/guapisolo/p/9696988.html