题目描述:
Mirko找到了N个盒子,盒子里面放了一些玩具。一共有M种不同的玩具,每种玩具可能有多个。同一种玩具可以出现在不同的盒子里。现在Mirko想选择一些盒子,使得每种玩具都至少有被选择。问Mirko有多少种选择的方法。
输入:第一行有2个整数N,M(1<=N<=1000000,1<=M<=20)
接下来N行,每行有若干个数。第一个数为Ki(0<=ki<=M),表示该盒子中有Ki种玩具。接下来有Ki个不同的数,表示有哪些种类的玩具。
输出:只有一行,表示方案总数模1000000007的值。
题目分析:
如果按照每个盒子暴力添加背包是 O ( N ∗ 2 M ) O(N*2^M) O(N∗2M)的。
“每种玩具都至少有被选择”这样的表述让我们想到容斥。如果把 m m m种玩具的第 i i i种没有被选择看作性质 i i i,那么要求的就是所有选择盒子的方案中一条性质也不满足的。
那么答案 = 所有选择盒子的方案 - 不满足性质1的 - 不满足性质2的… + 不满足性质1和性质2的 … 。
于是要求的就是玩具状态中至少某几位为0的选择盒子的方案数,记所有不包含这几位的玩具的盒子数为 c n t cnt cnt,那么方案数就是 2 c n t 2^{cnt} 2cnt。
于是问题化为求至少某几位为0的盒子的数量,这可以用高维前缀和在 O ( M ∗ 2 M ) O(M*2^M) O(M∗2M)的复杂度内求解。
Code:
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int n,m,f[1<<20],ans,bin[1<<20],pw[1000005];
int main()
{
scanf("%d%d",&n,&m);
pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2%mod;
ans=pw[n];
for(int i=1,k,x,s;i<=n;i++){
scanf("%d",&k),s=0;
while(k--) scanf("%d",&x),s|=1<<(x-1);
f[s]++;
}
for(int i=0;i<m;i++)
for(int s=1;s<1<<m;s++) if(s>>i&1)
f[s]+=f[s^(1<<i)];
for(int s=0;s<(1<<m)-1;s++){
bin[s]=bin[s>>1]+(s&1);
ans=(ans+((m-bin[s])&1?-1:1)*pw[f[s]])%mod;
}
printf("%d\n",(ans+mod)%mod);
}