【JZOJ3170】挑选玩具

版权声明:欢迎dalao指出错误暴踩本SD,蒟蒻写的博客转载也要吱一声 https://blog.csdn.net/enjoy_pascal/article/details/89366186

description

ABC找到N个箱子,箱子里装着一些玩具,一共有M种玩具,编号从1到M,同一种玩具可能出现在多个箱子里。

ABC决定从中选择一些箱子,把这些箱子中的玩具聚集到一起,必须保证每种玩具至少出现一次。

问ABC一共有多少种选择方案。


analysis

  • 对于 2 m 2^m 种玩具的选择情况,可以 O ( 2 m ) O(2^m) 枚举哪些位没有玩具算答案

  • 哪些位没有玩具,相当于其他位都有玩具,本质上一样

  • f [ S ] f[S] 表示选 S S 集合玩具的方案数,那么 f [ 2 m S 1 ] f[2^m-S-1] 表示不选 S S 集合玩具的方案数

  • 对于分治求 f f ,可以发现 f [ i ] & f [ i + m i d ] = f [ i ] f[i]\&f[i+mid]=f[i] ,那么把左半边累加到相应的右半边即可

  • 答案为 ( 1 ) S 2 f [ 2 m S 1 ] \sum(-1)^{|S|}2^{f[2^m-S-1]}


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 1000005
#define MAXM (1<<20)+5
#define mod 1000000007
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll f[MAXM],g[MAXM],pow[MAXN];
ll n,m,MAX,ans;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline void dfs(ll l,ll r)
{
	if (l==r){f[l]=g[l];return;}
	ll mid=(l+r)>>1;
	dfs(l,mid),dfs(mid+1,r);
	fo(i,l,mid)f[i-l+mid+1]+=f[i];
}
O3 int main()
{
	//freopen("T2.in","r",stdin);
	n=read(),m=read(),MAX=1<<m;
	fo(i,1,n)
	{
		ll m=read(),S=0;
		while (m--)S|=1<<(read()-1);
		++g[S];
	}
	dfs(0,MAX-1),pow[0]=1;
	fo(i,1,n)pow[i]=pow[i-1]*2%mod;
	g[0]=1;
	fo(i,0,MAX-1)
	{
		g[i]=g[i>>1]*(i&1?-1:1);
		ans=(ans+g[i]*(pow[f[MAX-i-1]]-1)%mod)%mod;
	}
	printf("%lld\n",(ans%mod+mod)%mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/enjoy_pascal/article/details/89366186