CF449D Jzzhu and Numbers


题解

刚刚学习了高维前缀和
这道题就肥肠简单了

高维前缀和其实原理肥肠简单
就是每次只考虑一维,然后只做这一维的前缀和
最后求出的就是总前缀和了

那么对于这道题
也就很简单了
发现选择的所有数每一位都必须不能所有数都是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\n",ans) ;
	return 0 ;
}
posted @ 2019-03-13 21:57  beretty  阅读(157)  评论(0编辑  收藏  举报