[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);
}