[AGC038E] Gachapon

一、题目

点此看题

二、解法

这道题其实一点都不难,如果你熟悉基本的技巧就好推到爆炸。

看到这题可以大概反应出是 \(\min-\max\) 反演的模型,定义 \(\max(S)\) 表示获取(指 \(i\) 出现了 \(b_i\) 次)集合 \(S\) 的中元素的最大时间,\(\min(S)\) 表示获取集合 \(S\) 中元素的最小时间,直接上公式:

\[E(\max(S))=\sum_{T\subseteq S,S\not=\varnothing} E(\min(T))\cdot (-1)^{|T|+1} \]

现在的问题是求解 \(E(\min(T))\),考虑常见的期望\(-\)概率型拆分(出现了不知道多少次,十分重要!),我们把它转化成轮数 \(k\)\(k\geq 0\))还没有获取任何元素的概率,可以枚举每种元素出现的次数 \(c_i(c_i<b_i)\),设 \(w(S)=\sum_{i\in S} a_i\)

\[\prod_{i\in T,\sum c_i=k} (\frac{a_i}{w(T)})^{c_i}\cdot {k\choose c_1,c_2...c_{|T|}} \]

\(p=\frac{w(S)}{w(T)}\) 表示要取多少次才能取到这个集合内(相当于一轮的代价),那么答案可以表示为:

\[\begin{aligned}&\sum_{T\subseteq S,T\not=\varnothing} (-1)^{|T|+1}\sum_{k=0} p\prod_{i\in T,\sum c_i=k} (\frac{a_i}{w(T)})^{c_i}\cdot {k\choose c_1,c_2...c_{|T|}}\\=&\sum_{T\subseteq S,T\not=\varnothing} (-1)^{|T|+1}\sum_{k=0} p\cdot w(T)^{-k}\cdot k!\prod_{i\in T,\sum c_i=k} \frac{a_i^{c_i}}{c_i!}\\\end{aligned} \]

受到 猎人杀 的启发,我们可以直接 \(dp\) 计算容斥系数,发现需要记录的只有 \(w(T)\)\(\sum c_i\),那么我们设 \(dp(i,j,k)\) 表示考虑前 \(i\) 个数,\(j=w(T),k=\sum c_i\) 的容斥系数,转移:

\[dp(i,j,k)\leftarrow dp(i-1,j,k) \]

\[dp(i,j,k)\leftarrow -\sum_{l=0}^{\min(b_i-1,k)} dp(i-1,j-a_i,k-l)\cdot\frac{a_i^l}{l!} \]

求出 \(dp\) 数组之后再套用式子计算即可,因为 \(\sum b\leq 400\),所以暴力转移时间复杂度 \(O(n^3)\)

#include <cstdio>
const int M = 405;
const int MOD = 998244353;
#define int long long
#define rep(i,s,t) for(int i=(s);i<=(t);i++) 
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,a[M],b[M],fac[M],inv[M],finv[M];
int ans,m1,m2,pw[M][M],dp[M][M][M];
void init(int n)
{
	inv[0]=inv[1]=finv[0]=fac[0]=1;
	rep(i,1,n) fac[i]=fac[i-1]*i%MOD;
	rep(i,2,n) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	rep(i,1,n) finv[i]=finv[i-1]*inv[i]%MOD;
}
signed main()
{
	n=read();init(m=400);
	rep(i,1,n)
	{
		m1+=a[i]=read();m2+=b[i]=read();pw[i][0]=1;
		rep(j,1,b[i]) pw[i][j]=pw[i][j-1]*a[i]%MOD;
	}
	dp[0][0][0]=-1;
	rep(i,1,n) rep(j,0,m1) rep(k,0,m2)
	{
		dp[i][j][k]=dp[i-1][j][k];
		rep(l,0,b[i]-1) if(a[i]<=j && l<=k)
			dp[i][j][k]=(dp[i][j][k]-dp[i-1][j-a[i]][k-l]
			*pw[i][l]%MOD*finv[l])%MOD;
	}
	rep(j,1,m1)
	{
		int x=m1*inv[j]%MOD;
		rep(k,0,m2)
		{
			ans=(ans+x*fac[k]%MOD*dp[n][j][k])%MOD;
			x=x*inv[j]%MOD;
		}
	}
	printf("%lld\n",(ans+MOD)%MOD);
}
posted @ 2022-03-29 16:53  C202044zxy  阅读(98)  评论(0编辑  收藏  举报