[LuoguP6442][COCI 2011/2012 #6] KOŠARE

题目传送门
观察到\(m\)只有\(20\),考虑状压,把每个箱子看成一个二进制数。
记录\(g_i\)表示状态是\(i\)的子集的箱子个数,这个事是高维前缀和。
记录\(h_i\)表示钦定\(i\)集合内的玩具都不选,其他随便选的方案数。容易发现:\(h_i=2^{g_{U\bigoplus i}}-1\)
根据容斥原理,我们加上钦定没有不选的,减去钦定有一个不选的,加上钦定有两个不选的.....答案即为:

\[\sum_{S}(-1)^{|S|}h_S \]

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define ll long long
#define df long double
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x
#define i128 __int128

const int N = 2e6 + 10;
const int mod = 1e9 + 7;
const ll INF = 1e18;
const df eps = 1e-10;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, m;
ll g[N], res = 0;

ll quick(ll x, ll y){
	ll rs = 1;
	while(y){
		if(y & 1) rs *= x;
		x *= x;
		rs %= mod, x %= mod;
		y >>= 1;
	}
	return rs;
}

signed main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++){
		int k = read(), s = 0;
		while(k--){
			int x = read();
			s |= (1 << (x - 1));
		}
		g[s]++;
	}
	for(int i = 1; i <= m; i++){
		for(int j = 0; j < (1 << m); j++){
			if(! (j & (1 << (i - 1)))) continue;
			(g[j] += g[j ^ (1 << (i - 1))]) %= mod;
		}
	}
	for(int i = 0; i < (1 << m); i++){
		int op = 1;
		for(int j = 1; j <= m; j++) if(i & (1 << (j - 1))) op *= -1;
		(res += 1ll * op * (quick(2, g[((1 << m) - 1) ^ i]) - 1) % mod + mod) %= mod;
	}
	cout << res;
	return 0;
}
posted @ 2025-08-14 13:19  Lordreamland  阅读(6)  评论(0)    收藏  举报