tokiomarine2020 E O(rand)【容斥】

传送门

给定 \(n\) 元可重集合 \(A\) 和正整数 \(k,S,T\),求 \(A\) 有多少个 \(\le k\) 元子集 \(B\) 的按位与为 \(S\),按位或为 \(T\)

\(k\le n\le 50,A\subseteq[0,2^{18})\)


显然可以转化为 \(S=0,T=2^l-1\) 的情况,其中 \(l=\text{popcount}(S\oplus T)\)

条件即为所有位都至少出现一次但不全出现。

容斥一下,钦定一些位要么全出现要么全不出现,设这些位是 \(x\),则有 \(\forall y\in B,x\&y\) 都相等。

直接暴力,时间复杂度 \(O(n2^l)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 53, M = 1<<18;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
} int _n, n, k, S, T, a[N], c[M]; LL F[N], C[N][N];
int main(){
	read(_n); read(k); read(S); read(T);
	if((S | T) != T) return puts("0"), 0;
	while(_n --){
		int x; read(x);
		if(S == (S & x) && T == (T | x)) a[++n] = x;
	} for(int i = 0;i <= n;++ i)
		for(int j = *C[i] = 1;j <= i && j <= k;++ j)
			F[i] += C[i][j] = C[i-1][j] + C[i-1][j-1];
	int _ = S ^ T; LL ans = F[n];
	for(int u = _;u;u = u-1&_){
		LL res = 0;
		for(int i = 1;i <= n;++ i) ++c[a[i]&u];
		for(int i = 1;i <= n;++ i){
			int &tmp = c[a[i]&u];
			res += F[tmp]; tmp = 0;
		} ans += __builtin_popcount(u) & 1 ? -res : res;
	} printf("%lld\n", ans);
}
posted @ 2021-04-02 10:50  mizu164  阅读(196)  评论(0编辑  收藏  举报