把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【AT5202】[AGC038E] Gachapon(Min-Max容斥)

点此看题面

  • 有一个随机数生成器,每轮随机生成一个\([1,n]\)的整数,有\(\frac{a_i}{\sum a_i}\)的概率生成\(i\)
  • 求所有\(i\)的出现次数都达到各自\(b_i\)的期望轮数。
  • \(n,\sum A,\sum B\le400\)

\(Min-Max\)容斥

刚好想着要做一道\(Min-Max\)容斥题,没想到就撞上了这题。

像这种对出现次数有要求的问题一看就超级\(Min-Max\)容斥。

根据它的公式:

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

要求所有\(i\)都达成限制就是\(E(max(S))\),而集合\(T\)内只要有一个满足限制就是\(E(min(T))\),显然要好算许多。

式子推导

考虑如何计算\(T\),那么就枚举产生\(T\)集合中的数的轮数\(k\)

\(A=\sum_{i=1}^n a_i,s(T)=\sum_{i\in T}a_i\),由于一轮产生的数在\(T\)中的概率为\(\frac {s(T)}A\),因此实际上需要的轮数应该是\(k\times\frac{A}{s(T)}\)

而要求\(k\)轮有一个数达成限制的概率,实际上就是算每一轮都没有数达成限制的概率,那相当于把\(k\)分配到\(c_{1\sim |T|}\)使得所有\(c_i<b_i\)

于是列出式子:(\(B=\sum_{i\in T}(b_i-1)\)

\[\sum_{k=0}^B\frac A{s(T)}\sum_{\sum_{i\in T}c_i=k}\frac{k!}{\prod_{i\in T}c_i!}\prod_{i\in T}(\frac{a_i}{s(T)})^{c_i}\\ \sum_{k=0}^B\frac {Ak!}{s(T)^{k+1}}\sum_{\sum_{i\in T}c_i=k}\prod_{i\in T}\frac{a_i^{c_i}}{c_i!} \]

最后算上容斥的\(DP\)系数:

\[(-1)^{|T|+1}\sum_{k=0}^B\frac {Ak!}{s(T)^{k+1}}\sum_{\sum_{i\in T}c_i=k}\prod_{i\in T}\frac{a_i^{c_i}}{c_i!} \]

然后就是考虑这个式子的求值即可。

容斥的套路\(DP\)求解

发现这个式子中,\(k!\)\(s(T)^{k+1}\)算是比较麻烦的项,因此我们考虑把\(s(T)\)\(k\)两个值设入\(DP\)的状态中。

那么只要设\(f_{i,j,k}\)表示处理了前\(i\)个数,选出的\(\sum a_i=j\),选择的\(\sum c_i=k\)\((-1)^{|T|+1}\prod\frac{a_i^{c_i}}{c_i!}\)的总和。

转移就分两种状态,不选或是选\(l\)个,后者需要给状态乘上容斥系数\(-1\)

\(DP\)的转移和最后的求解应该都是非常简单的,细节之类的也没啥吧。

就是要注意\(DP\)的初值为\(f_{0,0,0}=(-1)^{0+1}=-1\)

复杂度分析一下,是\(O(nABl)\)的,但实际上能选择的\(\sum l=B\),因此复杂度是\(O(AB^2)\)

代码:\(O(AB^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 400
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
int n,a[N+5],b[N+5],f[N+5][N+5][N+5],Fac[N+5],IFac[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
	RI i,j,k,l,t,A=0,B=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",a+i,b+i);
	for(Fac[0]=i=1;i<=N;++i) Fac[i]=1LL*Fac[i-1]*i%X;//预处理阶乘
	for(IFac[i=N]=QP(Fac[N],X-2);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;//预处理阶乘逆元
	for(f[0][0][0]=X-1,i=1;i<=n;A+=a[i],B+=b[i++]-1) for(j=0;j<=A;++j) for(k=0;k<=B;++k)//容斥的DP求解
	{
		Inc(f[i][j][k],f[i-1][j][k]);//不选
		for(t=1,l=0;l^b[i];t=1LL*t*a[i]%X,++l) Dec(f[i][j+a[i]][k+l],1LL*f[i-1][j][k]*t%X*IFac[l]%X);//选l个
	}
	for(t=0,j=1;j<=A;++j) for(k=0;k<=B;++k)//最后答案的求解
		Inc(t,1LL*A*Fac[k]%X*QP(QP(j,X-2),k+1)%X*f[n][j][k]%X);return printf("%d\n",t),0;//利用j和k计算
}
posted @ 2021-04-09 07:35  TheLostWeak  阅读(60)  评论(0编辑  收藏  举报