luogu CF449D Jzzhu and Numbers

luogu CF449D Jzzhu and Numbers

题目大意

求从 { a i } \{ ai \} 里面选出一个非空子集使这些数按位起来为0的方案数

题解

方案数???
DP???

数据范围这么大??!!

不可做

考虑求问题的补集,and值不为0的有多少个

发现好像不是很好求

考虑DP

f [ S ] a n d i f[S]表示and值包含i的个数

很容易发现 f [ S ] = j = 0 20 f [ S ( 1 < < j ) ] f[S] = \sum\limits_{j=0}^{20}f[S | (1<<j)]

然后就可以十分愉快地DP出来啦(其实这叫做高位前缀和)

那怎么求不为0的方案数呢?

考虑容斥

按照二进制1出现的次数来容斥

然后这题就没了

#include<bits/stdc++.h>
#define int long long
#define mod 1000000007
#define N 8000005
using namespace std;
int qpow(long long x, int y){
	long long ret = 1;
	for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
	return ret;
}
void fwt(int *a, int len, int opt){
	for(int j = 0; j <= 20; j ++)
		for(int i = 0; i < len; i ++)	
			if(!((1 << j) & i)) a[i] = (a[i] + opt * a[i | (1 << j)] + mod) % mod;
}
int pw[N], f[N], a[N], n;
 main(){
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++)
		scanf("%lld", &a[i]);
	for(int i = 1; i <= n; i ++) f[a[i]] ++;
	fwt(f, 1 << 20, 1);
	for(int i = 0; i < (1 << 20); i ++) f[i] = qpow(2, f[i]);
	long long ans = 0;
	for(int i = 0; i < (1 << 20); i ++){
		int bit = 0;
		for(int j = 0; j <= 20; j ++)  bit += (i >> j) & 1;
		if(bit & 1) ans -= f[i];
		else ans += f[i];
		ans = (ans + mod) % mod;
	}
	printf("%lld", ans);
	return 0;
}


发布了157 篇原创文章 · 获赞 90 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_38944163/article/details/101770291
今日推荐