题目大意:
给出一个长度为n的序列,构造出一个序列使得它们的位与和为0,求方案数
也就是从序列里面选出一个非空子集使这些数按位与起来为0.
看了好久才明白题解在干嘛,我们先要表示出两两组合位与和为0的所有情况
先hx一下每个数出现的次数,然后我们从遍历 i ,i 是二进制的数位
然后遍历所有的情况,如果第 i 位有1,那么说明我们去掉第 i 位的1就是又一种情况!
其实我们统计的是所有数在删掉/不删掉每一位的1 所有可能出现的数!
那么,状态内任意组合,不能取空集,总数就是
再根据容斥原理(最玄学的一步),根据状态内1的数量进行容斥,即可得到答案
仍然不是很理解,明天再思考思考
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
#define N (1<<20)+100
#define maxn 1000000
#define mod 1000000007
using namespace std;
int n;
int hx[N];
ll xx,yy,tt;
ll pw[N],sum[N],ans[22];
void get_pw() {pw[0]=1;for(ll i=1;i<=n+1;i++) pw[i]=(pw[i-1]*(ll)2)%mod;}
int main()
{
freopen("data.in","r",stdin);
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
hx[x]++;
sum[x]=hx[x];
}
for(int j=0;j<20;j++)
{
for(int s=(1<<20)-1;s>=0;s--)
if(s&(1<<j)) sum[s^(1<<j)]+=sum[s];
}
get_pw();
for(int s=0;s<(1<<20);s++)
{
int cnt=0;
for(int j=0;j<20;j++)
if(s&(1<<j))
cnt++;
ans[cnt]+=((pw[sum[s]]-1)%mod+mod)%mod;
ans[cnt]%=mod;
}
ll ret=0;
for(int i=0;i<20;i++)
{
if(i&1) ret-=ans[i],ret%=mod;
else ret+=ans[i],ret%=mod;
}
printf("%lld\n",(ret%mod+mod)%mod);
return 0;
}