码迷,mamicode.com
首页 > 其他好文 > 详细

CF449D Jzzhu and Numbers (状压DP+容斥)

时间:2018-09-24 23:27:39      阅读:427      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!