题解
刚刚学习了高维前缀和
这道题就肥肠简单了
高维前缀和其实原理肥肠简单
就是每次只考虑一维,然后只做这一维的前缀和
最后求出的就是总前缀和了
那么对于这道题
也就很简单了
发现选择的所有数每一位都必须不能所有数都是1
那么可以考虑一个简单的容斥
设(g_i)表示至少(i)的二进制下的这几维为1的方案数
那么就可以用类似高维前缀和来统计(g_i)
也就是统计ta作为哪些元素的子集
然后枚举选那几位
答案就是((-1)^{|S|}{2^{g_{i}}})
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 2000005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , val[M] , f[M] , pw2[M] , ans ;
inline int chk(int S) {
int ret = 0 ;
for(int i = 1 ; i <= 20 ; i ++)
if(S & (1 << (i - 1)))
++ ret ;
return ret ;
}
int main() {
n = read() ;
for(int i = 1 ; i <= n ; i ++) {
val[i] = read() ;
f[val[i]] ++ ;
}
pw2[0] = 1 ;
for(int i = 1 ; i <= n ; i ++) pw2[i] = 1LL * pw2[i - 1] * 2 % mod ;
for(int i = 0 ; (1 << i) <= 1000000 ; i ++) {
for(int j = (1 << 20) - 1 ; j >= 0 ; j --)
if(!(j & (1 << i)))
f[j] = (f[j] + f[j ^ (1 << i)]) % mod ;
}
for(int i = 0 , sz ; i < (1 << 20) ; i ++) {
sz = chk(i) ;
if(sz & 1) sz = -1 ;
else sz = 1 ;
ans = ((ans + sz * (pw2[f[i]] - 1)) % mod + mod) % mod ;
}
printf("%d
",ans) ;
return 0 ;
}