[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;
}
$\color{blue} \mathcal {Lordreamland}$

浙公网安备 33010602011771号