正题
题目大意
有nnn个箱子放了若干个玩具,要求选择一些箱子使得mmm种玩具都有,求方案总数。
解题思路
设fSf_SfS表示选择只有在集合为SSS的方案数。
然后答案考虑容斥,那么答案就是∑S(2(f(∼S))−1)∗(−1)∣S∣\sum_S (2^{(f_{(\sim S)})}-1)*(-1)^{|S|}S∑(2(f(∼S))−1)∗(−1)∣S∣
我们将集合的表示状压起来。
现在考虑如何快速求fff数组,首先fS=cSf_S=c_SfS=cS(cSc_ScS表示集合是SSS的箱子个数)。
然后分治,每次分治时rrr肯定是若干个111
比如当l=0,r=11111l=0,r=11111l=0,r=11111时左边边都是0XXXX0XXXX0XXXX而右边是1XXXX1XXXX1XXXX。也就是右边是左边第一位变成一。那么对于每个区间就有
fi=∑mfi−m+l−1(i>m)f_i=\sum_mf_{i-m+l-1}(i> m)fi=m∑fi−m+l−1(i>m)
时间复杂度O(2mlog2m)O(2^m\ log\ 2^m)O(2m log 2m)
codecodecode
#include<cstdio>
using namespace std;
const int M=(1<<20)+10,XJQ=1e9+7;
int n,m,f[M],w[M],p[M],ans,v[M],MS;
void apart(int l,int r)
{if(l==r){f[l]=v[l];return; } int m=(l+r)>>1;apart(l,m);apart(m+1,r);for(int i=l;i<=m;i++)f[i+m-l+1]+=f[i];
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int z=0,k;scanf("%d",&k);while(k--){int x;scanf("%d",&x);z|=(1<<x-1);}v[z]++;}MS=1<<m;for(int i=0;i<MS;i++)w[i]=w[i>>1]^(i&1);p[0]=1;for(int i=1;i<=n;i++)p[i]=(p[i-1]<<1)%XJQ;apart(0,MS-1);for(int i=0;i<MS;i++)(ans+=((w[i]?-1:1)*(p[f[MS-i-1]]-1)))%=XJQ;printf("%d",(ans+XJQ)%XJQ);
}