1. 程式人生 > >CF449D Jzzhu and Numbers (狀壓DP+容斥)

CF449D Jzzhu and Numbers (狀壓DP+容斥)

進行 狀態 位與 不能 gif using com 答案 {}

題目大意:

給出一個長度為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+容斥)