【JZOJ3170】挑选玩具【分治】
题目大意:
题目链接:https://jzoj.net/senior/#main/show/3170
有个箱子装着个玩具(一个玩具可以在多个箱子内),求有多少种选择箱子的方案使得每种玩具至少有一个。
思路:
设表示,也就是选择其中一些箱子,会得到一个玩具集合(状压后),如果完全包含,就加一。
那么就是没有一个玩具在集合中的方案数。
显然答案就是
对于求,可以考虑使用分治。
显然,因为若是,则是
这样时间复杂度就降到了。
代码:
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1000010,MOD=1e9+7,M=(1<<20)+10;
int n,m,ans,MAXN,cnt[M],f[M],power[N];
void work(int l,int r)
{
if (l==r)
{
f[l]=cnt[l];
return;
}
int mid=(l+r)/2;
work(l,mid);
work(mid+1,r);
for (int i=l;i<=mid;i++)
f[i-l+mid+1]+=f[i];
}
int main()
{
freopen("data","r",stdin);
scanf("%d%d",&n,&m);
MAXN=(1<<m);
for (int i=1;i<=n;i++)
{
int sum,x,S=0;
scanf("%d",&sum);
while (sum--)
{
scanf("%d",&x);
S|=(1<<x-1);
}
cnt[S]++;
}
work(0,MAXN-1);
power[0]=1;
for (int i=1;i<=n;i++) power[i]=power[i-1]*2%MOD;
cnt[0]=1;
for (int i=0;i<MAXN;i++)
{
if (i&1) cnt[i]=-cnt[i>>1];
else cnt[i]=cnt[i>>1];
ans=(ans+cnt[i]*(power[f[MAXN-1-i]]-1)%MOD)%MOD;
}
printf("%d\n",(ans%MOD+MOD)%MOD);
return 0;
}