Codeforces 850F. Rainbow Balls

貌似没看到有人用条件概率做这道题的,来整一个

题目链接:F - Rainbow Balls

题目大意:一开始有 n 种不同颜色的球,第 i 种颜色有 ai 个。每次操作随机先后选取两个球,并将后取出来的球颜色染成前者的颜色。问期望进行多少次操作后所有球的颜色均相同。

符号约定与解释

P(x):事件 x 发生的概率

P(x,y):事件 x,y 同时发生的概率

P(x|y):在事件 y 发生的条件下,事件 x 发生的概率。显然有 P(x|y)=P(x,y)P(y)

Pi,j:某时刻某颜色的个数为 i,且下一时刻个数为 j 的概率。显然只有 j=i1,i,i+1 是有效的

Ni:某颜色的球个数为 i

xk:第 k 时刻某颜色的个数

Fi:最终局面为所有球都变为第 i 种颜色这一事件,我们也称为第 i 种颜色获胜

E(Ni|Fj):当前颜色为 j 的球的个数为 i,在事件 Fj 发生的条件下,期望的操作次数

Pi,j|Fk:在事件 Fk 发生的条件下,发生颜色个数从 ij 转移的概率

可以得出,最终答案即为 i=1nE(Nai|Fi)×P(Fi)

弱化版本

先来考虑这么一个弱化版本的问题:若一开始每种颜色的球都恰好有一个怎么做?

此时,根据对称性质,P(Fi) 固定为 1n,于是此时答案就为 i=1nE(N1|Fi)×1n=E(N1|F1)

而对任意 i[1,n),有:

(1)E(Ni|F1)=1+E(Ni1|F1)×Pi,i1|F1+E(Ni|F1)×Pi,i|F1+E(Ni+1|F1)×Pi,i+1|F1

这里需要注意的是,P1,0|F1 必然是为 0 的,这是因为我们已经制定了 F1 这一前提条件,所以对应颜色的球个数是不可能为零的。

于是这题的瓶颈就变成了求 Pi,j|F1,以求 Pi,i1|F1 为例,我们首先可以将其改写成 P(xk+1=i1|xk=i,F1),现在我们开始求对应的值。

P(xk+1=i1|xk=i,F1)=P(xk+1=i1,xk=i,F1)P(xk=i,F1)=P(xk+1=i1,F1|xk=i)×P(xk=i)P(F1|xk=i)×(xk=i)=P(F1|xk+1=i1,xk=i)×P(xk+1=i1|xk=i)P(F1|xk=i)=i1n×i(ni)n(n1)in=(ni)×(i1)n(n1)

过程中化简的思路大致就是把公共项提出来,然后变成计算 P(F1|xk=i),即某时刻颜色 1 的个数为 i 时,其获胜的概率。对应值的计算也很简单,因为 n 个颜色不同的球每个球的获胜概率均为 1n,那么此情况下可以看做有其中 i 个颜色被当作颜色 1,于是 P(F1|xk=i)=in

采用类似的计算思路,最终我们可以得出:

Pi,i1|F1=(ni)×(i1)n(n1),Pi,i+1|F1=(ni)×(i+1)n(n1),Pi,i|F1=1(ni)×2in(n1)

那么令 fi=E(Ni|F1),将结果代入式 (1),可以得到如下结果:

fi=1+(ni)×(i1)n(n1)fi1+(ni)×(i+1)n(n1)fi+1+(1(ni)×2in(n1))fi

(ni)×2in(n1)fi=1+(ni)×(i1)n(n1)fi1+(ni)×(i+1)n(n1)fi+1

(2)2i(ni)fi=n(n1)+(ni)(i1)fi1+(ni)(i+1)fi+1

根据初始条件 fn=0,我们最终可以推出 f1=(n1)2,具体过程放在最后。

回到本题

m=i=1nai ,那么可以得出 P(Fi)=aim。而根据对称性,E(Nai|Fi)=E(Nai|F1)。于是我们就可以得到 ans=i=1nfai×aim,而 fi 的值可以直接根据 (2) 式递推求出,最终我们就能以 O(max(ai)logM) 的时间复杂度解决此题。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define LL long long
#define MOD 1000000007
int n,m,x,c[N],f[N],ans;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d",&x);
		c[x]++;
		n+=x;
	}
	f[1]=1ll*(n-1)*(n-1)%MOD;
	for(int i=1;i<N-5;i++){
		LL tot=2ll*i*(n-i)%MOD*f[i]%MOD;
		tot=(tot+MOD-1ll*(i-1)*(n-i)%MOD*f[i-1]%MOD)%MOD;
		tot=(tot+MOD-1ll*n*(n-1)%MOD)%MOD;
		tot=tot*qow(i+1,MOD-2)%MOD*qow(n-i,MOD-2)%MOD;
		f[i+1]=tot;
		ans=(ans+qow(n,MOD-2)*i%MOD*f[i]%MOD*c[i])%MOD;
	}
	printf("%d\n",ans);
}

推出 f1 的过程

2i(ni)fi=n(n1)+(ni)(i1)fi1+(ni)(i+1)fi+1

2ifi=n(n1)ni+(i1)fi1+(i+1)fi+1

ti=ifi,对 i[1,n) 就有 2ti=n(n1)ni+ti1+ti+1,且 t0=tn=0

那么设 ti=aiti1+bi,借由 tn=0 可以推出 an1=12,bn1=n(n1)2。考虑找到 ai,bi 的递推式从而推出 a2,b2 的值,与 i=1 的情况 2t1=n+t2 联立求解

2ti=n(n1)ni+ti1+ti+1=n(n1)ni+ti1+ai+1ti+bi+1

ai=12ai+1,bi=12ai+1(bi+1+n(n1)ni)

手推几项不难瞪眼看出 ai=nini+1,bi=nini+1n(n1),于是就有

{2t1=n+t2t2=n2n1t1+n(n2)

最后解得 f1=t1=(n1)2

posted @   DeaphetS  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示