[AGC038E] Gachapon
VI.[AGC038E] Gachapon
因为模型同III.重返现世长得很像,所以我们也来考虑minmax容斥。
首先,我们仍然翻出式子
然后,我们考虑计算 \(\text E(\min\mathbb T)\)。
我们发现,对于某个 \(\mathbb T\),它的 \(\min\) 的实际意义是其中一个元素率先全部被生成。而当该元素被生成完时,其它 \(\mathbb T\) 中元素生成的数量都不能达到 \(b_i\)。因此这启发我们要枚举结束时 \(\mathbb T\) 中每个元素被生成的数量。设此数组为 \(\{c\}\)。
我们发现,由 \(\mathbb T\) 来推出所有合法的 \(\{c\}\) 是困难的,但是由 \(\{c\}\) 来反推 \(\mathbb T\) 则是简单的——因为在处理 \(\{c\}\) 的时候肯定顺手就把 \(\mathbb T\) 的位置给记录了。于是我们考虑给定了一个 \(\{c\}\),计算其作为其对应的 \(\mathbb T\) 的终止状态的概率。
首先,我们考虑令 \(a'\) 表示那个率先被全部生成的数的 \(a\)。显然,\(c'=b'\),但是因为钦定了最后一个生成的数是 \(a'\),因此我们令 \(c'\) 强制减一,这样现在所有 \(c\) 中元素间顺序便可以任意调换。
考虑恰好生成 \(c\) 中所有元素的概率,是
但是,因为元素间有序,所以还要乘上多项式系数
同时,别忘记最后一个生成的数必须是 \(a'\),因此还有
考虑生成此种情形期望需要生成多少个数:因为期望 \(\dfrac{S}{\sum a}\)(依照题面,有定义 \(S=\sum\limits_{i=0}^{n-1}a_i\))次会摇到一个来自 \(\mathbb T\) 中的元素,所以期望 \((\sum c+1)\times\dfrac S{\sum a}\) 次就能摇全。
所有东西怼一块,得到
稍微整理一下,把与单个的 \(a\) 和 \(\sum a\) 有关的拆开来放
最开头的是常数,中间一个分数是与 \(\sum c,\sum a\) 有关的东西,最后一坨是与单个的 \(a\) 与 \(c\) 有关的东西。
于是我们考虑DP。设 \(f_{i,j,k,odd,0/1}\) 表示当前DP到位置 \(i\),\(\sum c=j,\sum a=k\),\(|\mathbb T|\) 的奇偶性为 \(odd\),且 \(a'\) 未/已被决定。
转移分两种:确定 \(a'\) 的转移和不确定 \(a'\) 的转移。确定 \(a'\) 的转移就强制你有 \(c=b-1\)(注意到我们之前已经令 \(c'\) 强制减一了),不确定 \(a'\) 的转移,可以枚举 \(c\in[0,b)\)。
乍一看,\(i,j,k\) 三维都是 \(400\),剩下两维都是常数,但还要枚举一个 \(c\),这不是 \(400^4\) 的吗?
一开始我也苦恼了很久,但后来想到 \(i\) 维和 \(c\) 维加在一块等于 \(\sum b=400\),因此实际是 \(400^3\) 的。
注意将一些共同的东西提前算出可以减少常数。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int ksm(int x,int y=mod-2){int z=1;for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;return z;}
int n,a[410],b[410],A,B,f[410][410][2][2],fac[410],inv[410],res;
void ADD(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]),A+=a[i],B+=b[i];
fac[0]=1;for(int i=1;i<=B;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[B]=ksm(fac[B]);for(int i=B;i;i--)inv[i-1]=1ll*inv[i]*i%mod;
// for(int i=0;i<=B;i++)printf("%d ",fac[i]);puts("");
// for(int i=0;i<=B;i++)printf("%d ",inv[i]);puts("");
f[0][0][0][0]=1;
for(int i=1;i<=n;i++){
int tmp=1ll*ksm(a[i],b[i]-1)*inv[b[i]-1]%mod*a[i]%mod;
for(int j=B;j>=0;j--)for(int k=A;k>=0;k--)for(int odd=0;odd<2;odd++){
// if(f[j][k][odd][0])printf("(%d,%d,%d,%d,%d):%d\n",i-1,j,k,odd,0,f[j][k][odd][0]);
// if(f[j][k][odd][1])printf("(%d,%d,%d,%d,%d):%d\n",i-1,j,k,odd,1,f[j][k][odd][1]);
if(f[j][k][odd][0]){
// printf("%d %d %d %d\n",f[j][k][odd][0],ksm(a[i],b[i]-1),inv[b[i]-1],a[i]);
ADD(f[j+b[i]-1][k+a[i]][odd^1][1],1ll*f[j][k][odd][0]*tmp%mod);
for(int l=0,m=1;l<b[i];l++,m=1ll*m*a[i]%mod)ADD(f[j+l][k+a[i]][odd^1][0],1ll*f[j][k][odd][0]*m%mod*inv[l]%mod);
}
if(f[j][k][odd][1])for(int l=0,m=1;l<b[i];l++,m=1ll*m*a[i]%mod)ADD(f[j+l][k+a[i]][odd^1][1],1ll*f[j][k][odd][1]*m%mod*inv[l]%mod);
}
}
for(int j=0;j<=B;j++)for(int k=1;k<=A;k++)for(int odd=0;odd<2;odd++){
int tmp=1ll*f[j][k][odd][1]*(j+1)%mod*A%mod*ksm(ksm(k,j+2))%mod*fac[j]%mod;
// printf("%d,%d,%d:%d\n",j,k,odd,tmp);
// printf("%d %d %d %d %d\n",f[j][k][odd][1],(j+1),A,ksm(ksm(k,j+2)),fac[j]);
if(odd&1)(res+=tmp)%=mod;else (res+=mod-tmp)%=mod;
}
printf("%d\n",res);
return 0;
}