[ABC215H] Cabbage Master

一、题目

以后 \(\tt ABC\) 的后两题还是都做一做,拿到这题真的不是挺会的\(...\)

点此看题

\(n\) 种颜色 \(m\) 个盒子,第 \(i\) 种颜色有 \(a_i\) 个球,若 \(c_{i,j}=1\) 那么第 \(i\) 种颜色的球可以放进盒子 \(j\) 中,如果对于所有盒子 \(j\) 最后被放进的球数 \(\geq b_j\) 那么 \(\tt zxy\) 会成为常数大师球互不相同,问有多少个取走最少球的方案使得 \(\tt zxy\) 不当常数大师,答案模 \(998244353\)

\(n\leq 20,m\leq 10^4\)

二、解法

根据 \(\tt Hall\) 定理,所有盒子满足条件的充要条件是,设颜色集合为 \(A\),盒子集合为 \(B\)\(ad(S)\) 表示和盒子有连边的颜色集合:

\[\forall S\in B,\sum_{i\in S}b_i\leq \sum_{i\in ad(S)} a_i \]

首先考虑怎么求出取走的最少球数,考虑枚举颜色集合 \(T\),然后把 \(ad(i)\in T\)\(b_i\) 全部考虑进去,再考虑取走有关的 \(a\) 使得上面的条件不合法即可:

\[mi=\max(0,\min_{T}\{\sum_{i\in T}a_i-\sum_{ad(i)\in T}b_i+1\}) \]

然后考虑计算答案,设集族 \(\mathcal{S}=\arg \min_T\{\sum_{i\in T}a_i-\sum_{ad(i)\in T}b_i+1\}\),枚举颜色集合 \(T\) 表示取其中的球,那么当且仅当 \(T\) 被集族 \(\mathcal{S}\) 中某个集合包含的时候可以达到取球数最小值:

\[\sum_{T\in A}[\exist S\in\mathcal{S},T\in S]f(T,c) \]

其中 \(f(T,c)\) 表示从集合 \(T\) 中取出 \(c\) 个球,其中每个颜色都必须要取出一次的方案数,可以用容斥钦定某个颜色集合 \(P\) 不选,然后剩下的无限制(但是下面柿子枚举的是无限制的集合):

\[f(T,c)=\sum_{P\in T}(-1)^{|T|-|P|}{\sum_{i\in P} a_i\choose c} \]

把全部需要枚举子集的地方都换成 \(\tt fwt\) 即可,时间复杂度 \(O(2^nn)\)

三、总结

这道题一开始不知道如何下手,这种"任意"的条件,可以考虑是在哪个部分将它破坏的。

这题还很容易算重,我们要把区分度搞出来,比如枚举颜色集合 \(T\) 那么只能选这个集合的球并且每个颜色都至少取一次,这样不同的 \(T\) 对应的方案数就一定是不一样的。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 2000005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,mi,a[M],b[M],sum[M],tot[M],st[M];
int ans,fac[M],inv[M],f[M],g[M],bit[M];
void init(int n)
{
	fac[0]=inv[0]=inv[1]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD; 
}
int C(int n,int m)
{
	if(n<m || m<0) return 0;
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
void fwt(int *a,int n,int op)//0:and 1:or
{
	for(int i=1;i<n;i<<=1)
		for(int j=0,t=2*i;j<n;j+=t)
			for(int k=0;k<i;k++)
				if(op==1) a[i+j+k]+=a[j+k];
				else a[j+k]+=a[i+j+k];
}
signed main()
{
	n=read();m=read();k=1<<n;mi=1e9;
	init(2e6);
	for(int i=0;i<n;i++) a[i]=read();
	for(int i=1;i<=m;i++) b[i]=read();
	for(int i=0;i<n;i++)
		for(int j=1;j<=m;j++)
			if(read()) st[j]|=(1<<i);
	for(int i=0;i<k;i++)
		for(int j=0;j<n;j++)
			if(i&(1<<j)) tot[i]+=a[j];
	for(int i=1;i<=m;i++)
		sum[st[i]]+=b[i];
	fwt(sum,k,1);
	for(int i=0;i<k;i++)
		if(sum[i]) mi=min(mi,tot[i]-sum[i]+1);
	if(mi<=0) {puts("0 1");return 0;}
	for(int i=0;i<k;i++)
		bit[i]=bit[i>>1]+(i&1);
	for(int i=0;i<k;i++)
	{
		g[i]=(tot[i]-sum[i]+1==mi);
		f[i]=((bit[i]&1)?-1:1)*C(tot[i],mi);
	}
	fwt(g,k,0);
	fwt(f,k,1);
	for(int i=0;i<k;i++) if(g[i])
		ans=(ans+((bit[i]&1)?-1:1)*f[i])%MOD;
	printf("%lld %lld\n",mi,(ans+MOD)%MOD);
}
posted @ 2021-08-23 16:56  C202044zxy  阅读(104)  评论(1编辑  收藏  举报