AT_agc038_e [AGC038E] Gachapon 题解
比较基础的一道题。很容易想到 Min-Max 容斥:
\[E(\max(S))=\sum_{T\sube S}(-1)^{|T|-1}\times E(\min(T))
\]
然后发现 \(E(\min(T))=\sum_{k\ge 0}P(\min(T)\ge k)\)。考虑 dp,记 \(f_{i,j,k}\) 表示从前 \(i\) 个数中选出 \(T\),\(\sum_{i\in T}A_i=j,\sum_{i\in T}c_i=k\) 且每个数都还没被选过 \(B_i\) 次,到达这个状态的概率(\(c_i\) 表示第 \(i\) 个数选了多少次)。发现每次选出一个数的概率不好直接求得,可以先乘上对应的 \(A\) 值最后再除以一个东西。
转移时直接枚举当前数 \(i\) 选了 \(x\) 个,\(f_{i-1,j-a_i,k-x}\) 乘上系数 \({k\choose x}\times A_i^x\) 求和即可。
最后再考虑一个状态 \(f_{n,i,j}\) 对答案的贡献是什么。发现每次都有 \(\frac{S-i}S\) 的概率选中集合外的一个数,所以对于每一步的贡献还要加上 \(\frac{S-i}i\)。那么每一步的贡献就是 \(\frac S i\),所以最终答案就是 \(\sum S\times i^{j+1}\times f_{n,i,j}\)。时间复杂度 \(\mathcal O(\sum A_i(\sum B_i)^2)\)。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define md 998244353
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int n,s1,s2,a[403],b[403],c[403][403];
ll ans,p[403][403],dp[403][403][403];
ll power(ll x,int y){
ll ans=1;
for(;y;y>>=1){
if(y&1)ans=ans*x%md;
x=x*x%md;
}
return ans;
}
signed main(){
cin>>n;
rep(i,1,n)cin>>a[i]>>b[i];
c[0][0]=1;
rep(i,1,400){
p[i][0]=c[i][0]=1;
rep(j,1,401){
p[i][j]=p[i][j-1]*i%md;
c[i][j]=(c[i-1][j-1]+c[i-1][j])%md;
}
}
dp[0][0][0]=-1;
rep(i,1,n){
s1+=a[i],s2+=b[i]-1;
rep(j,0,s1){
rep(k,0,s2){
dp[i][j][k]=dp[i-1][j][k];
if(j>=a[i])rep(x,0,min(k,b[i]-1)){
dp[i][j][k]=(dp[i][j][k]-dp[i-1][j-a[i]][k-x]*p[a[i]][x]%md*c[k][x])%md;
}
}
}
}
rep(i,1,s1)rep(j,0,s2){
ans=(ans+s1*dp[n][i][j]%md*power(p[i][j+1],md-2))%md;
}
cout<<(ans+md)%md;
return 0;
}