题意:给出一个n,随后给出n个数a[i],问你这些数的非空子集所有元素&运算后等于0的集合个数(即ai1 & ai2 & ... & aik = 0 (1 ≤ k ≤ n)),结果对1e9取模。
题解:对于这题的解法直接给出是dp+容斥,后面再说明为什么使用到容斥。
定义:s[i]:若i的二进制中1的个数为奇数个时为1,反之为0
g[i]:表示pow(2,i)
f[i]:表示与i进行&运算结果为i的a[i]个数
下面是对第二组样例的解释:
a[i] 0 1 2 3
二进制表示 000 001 010 011
s[i] 0 1 1 2
二进制第0位时f[i] 1 1 1 1
二进制第1位时f[i] 2 1 2 1
二进制第2位时f[i] 4 2 2 1
容斥符号 + - - +
ans=(2^4-1)-(2^2-1)-(2^2-1)+(2^1-1) = 15-3-3+1 = 10
对每个数的二进制数1的个数进行奇偶容斥,公式为
(为啥是2^20呢?因为1e6的数据范围)
样例推导:(f[i]存的是个数,下面的是还原个数的内容并列出子集的情况,即2^f[i]-1)
对于f[0]:0 1 2 3 01 02 03 12 13 23 012 023 013 123 0123
对于f[1]:1 3 13
对于f[2]:2 3 23
对于f[3]:3
综上:可以看出我们要得到的ans是黄色部分,对于f[0]的总情况需要去掉绿色、蓝色的组合,但是对于f[1]、f[2]时蓝色减多了一次,因此我们才需要考虑每个数的二进制数的1的个数分奇偶容斥来求和得到结果。
代码如下:
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<time.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 4e6;
const ll mod = 1e9 + 7;
ll dp[maxn];
ll g[maxn];
int s[maxn];
void init() {
g[0] = 1;
for (int i = 1; i <= (1 << 20); i++)
g[i] = (g[i - 1] * 2LL) % mod;
}
void fun() {
memset(s, 0, sizeof(s));
for (int i = 1; i <= 20; i++)
for (int j = 0; j <= (1 << 20); j++) {
if (j&(1 << (i - 1)))//表示j的二进制第i位为1
s[j] ^= 1;
else
dp[j] = (dp[j] + dp[j | (1 << (i - 1))]) % mod;
}
}
int main()
{
init();
int n, x;
while (~scanf("%d", &n)) {
memset(dp, 0, sizeof(dp));
while (n--) {
scanf("%d", &x);
dp[x]++;
}
fun();
ll ans = 0;
for (int i = 0; i <= (1 << 20); i++) {
if (s[i]) ans = ((ans - (g[dp[i]] - 1)) % mod + mod) % mod;
else ans = (ans + (g[dp[i]] - 1)) % mod;
}
cout << ans << endl;
}
}