(DP+容斥)
题意:给出一个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; } }