Luogu 6442 [COCI2011-2012#6] KOŠARE
简单题。
发现 很小,所以一个箱子可以用一个二进制数 表示,值域 。然后就变成取出若干个 使得或起来为全集的方案数。
将所有 按位取反,即求若干个 与起来为空集的方案数,就是这题。
考虑容斥,令 为与起来恰为 的方案数, 为与起来包含 的方案数。显然有 ,反演得到 。由于我们要求 ,即为 。由于可以 枚举 ,我们现在考虑如何求 。
由于与起来包含 ,所以你选出来每个数都包含 ,所以 , 为包含 的数的个数。
显然 是个超集和,想一下 FWT 求子集和的时候,把前面贡献后面,那么现在在后面贡献前面即可。
然后就做完了。复杂度 。
const int maxn = 2e6 + 100;
const int lg = 20;
const int S = (1 << lg) - 1;
const int mod = 1e9 + 7;
int n, k, a[maxn], c[S + 100], f[S + 100], g[S + 100];
int qpow(int p, int q) {
int res = 1;
while (q) {
if (q & 1) res = 1ll * res * p % mod;
p = 1ll * p * p % mod, q >>= 1;
}
return res;
}
int main() {
n = read(), k = read();
for (int i = 1; i <= n; i++) {
int p = read();
for (int j = 1; j <= p; j++)
a[i] |= (1 << (read() - 1));
c[(1 << k) - 1 - a[i]]++;
}
for (int i = 0; i <= S; i++) f[i] = c[i];
for (int o = 2, k = 1; o <= (1 << lg); o <<= 1, k <<= 1)
for (int i = 0; i < S; i += o)
for (int j = 0; j < k; j++)
(f[i + j] += f[i + j + k]) %= mod;
for (int i = 0; i <= S; i++) g[i] = (qpow(2, f[i]) + mod - 1) % mod;
int ans = 0;
for (int i = 0; i <= S; i++) {
int t = __builtin_popcount(i), tp = (t & 1) ? (mod - 1) : 1;
(ans += 1ll * tp * g[i] % mod) %= mod;
}
write(ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】