P1502【COCI 2011/2012 CONTEST #6-4】盒子与玩具 | |
|
问题描述
Mirko找到了N个盒子,盒子里面放了一些玩具。一共有M种不同的玩具,每种玩具可能有多个。同一种玩具可以出现在不同的盒子里。现在Mirko想选择一些盒子,使得每种玩具都至少有被选择。问Mirko有多少种选择的方法。
输入格式
第一行有2个整数N,M(1<=N<=1000000,1<=M<=20)
接下来N行,每行有若干个数。第一个数为Ki(0<=ki<=M),表示该盒子中有Ki种玩具。接下来有Ki个不同的数,表示有哪些种类的玩具。
输出格式
只有一行,表示方案总数模1000000007的值。
样例输入
3 3
3 1 2 3
3 1 2 3
3 1 2 3
样例输出
7
提示
50%的数据,N<=100,M<=15
70%的数据,N<=1000000 ,M<=15
来源 COCI 2011/2012 CONTEST #6
f[s] 表示选出盒子并集为S的方案数。
g[s]表示输入时统计的 集合为S的盒子的数量。
对g[s]进行枚举子集求和得到G[s].即G[s]= ∑{g[T]}(T是S的子集)
G[s]表示S及其子集代表的盒子数量。
对f[s]进行枚举子集求和得到F[s].即F[s]= ∑{f[ T ]}(T是S的子集)
F[s]表示选出盒子并集为 S或其子集 的方案数。
经过计算我们可以以发现F[s]= 2^G[s].
于是可以先求G[s],再求F[s],但是对于最大的数据时间还是比较长
考虑这样的方法:
每一次讨论某一个元素,设S含有i号元素。
g[s]+=g[s-(1<<i)] ,这样原地求出了G[s].
而f[s]-=f[s-(1<<i)],f[s]求法留给读者思考
#include<cstdio>
#include<iostream>
#define LL long long
#define mod 1000000007
using namespace std;
const LL maxn=(1<<23),maxm=1000005;
LL g[maxn],f[maxn],n,m,all,pow2[maxm];
inline void _read(LL &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
int main(){
_read(n);_read(m);
LL i,j,k,x,s;
pow2[0]=1;
for(i=1;i<=n;i++){
_read(k);
pow2[i]=(pow2[i-1]<<1)%mod;
LL o=0;
for(j=1;j<=k;j++){
_read(x);
x--;
o|=(1LL<<x);
}
g[o]++;
}
all=(1<<m)-1;
for(i=0;i<=m;i++)
for(s=0;s<=all;s++)
if(s&(1<<i))
(g[s]+=g[s^(1<<i)])%=mod;
for(s=0;s<=all;s++)
f[s]=pow2[g[s]];
for(i=0;i<=m;i++)
for(s=0;s<=all;s++)
if(s&(1<<i))
f[s]=(f[s]-f[s^(1<<i)]+mod)%mod;
cout<<f[all]%mod;
}