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

Posted guapisolo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF449D Jzzhu and Numbers (状压DP+容斥)相关的知识,希望对你有一定的参考价值。

题目大意:

给出一个长度为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
",(ret%mod+mod)%mod);
49     return 0;
50 }

 

以上是关于CF449D Jzzhu and Numbers (状压DP+容斥)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 449D:Jzzhu and Numbers

Codeforces 449D:Jzzhu and Numbers(高维前缀和)

Jzzhu and Numbers CodeForces - 449D (容斥,dp)

Jzzhu and Numbers CodeForces - 449D (高维前缀和,容斥)

(CF#257)B. Jzzhu and Sequences

Codeforces 449D. Jzzhu and Numbers