貌似没看到有人用条件概率做这道题的,来整一个
题目链接: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=i−1,i,i+1 是有效的
Ni:某颜色的球个数为 i
xk:第 k 时刻某颜色的个数
Fi:最终局面为所有球都变为第 i 种颜色这一事件,我们也称为第 i 种颜色获胜
E(Ni|Fj):当前颜色为 j 的球的个数为 i,在事件 Fj 发生的条件下,期望的操作次数
Pi,j|Fk:在事件 Fk 发生的条件下,发生颜色个数从 i 到 j 转移的概率
可以得出,最终答案即为 ∑ni=1E(Nai|Fi)×P(Fi)。
弱化版本
先来考虑这么一个弱化版本的问题:若一开始每种颜色的球都恰好有一个怎么做?
此时,根据对称性质,P(Fi) 固定为 1n,于是此时答案就为 ∑ni=1E(N1|Fi)×1n=E(N1|F1) 。
而对任意 i∈[1,n),有:
E(Ni|F1)=1+E(Ni−1|F1)×Pi,i−1|F1+E(Ni|F1)×Pi,i|F1+E(Ni+1|F1)×Pi,i+1|F1(1)
这里需要注意的是,P1,0|F1 必然是为 0 的,这是因为我们已经制定了 F1 这一前提条件,所以对应颜色的球个数是不可能为零的。
于是这题的瓶颈就变成了求 Pi,j|F1,以求 Pi,i−1|F1 为例,我们首先可以将其改写成 P(xk+1=i−1|xk=i,F1),现在我们开始求对应的值。
P(xk+1=i−1|xk=i,F1)=P(xk+1=i−1,xk=i,F1)P(xk=i,F1)=P(xk+1=i−1,F1|xk=i)×P(xk=i)P(F1|xk=i)×(xk=i)=P(F1|xk+1=i−1,xk=i)×P(xk+1=i−1|xk=i)P(F1|xk=i)=i−1n×i(n−i)n(n−1)in=(n−i)×(i−1)n(n−1)
过程中化简的思路大致就是把公共项提出来,然后变成计算 P(F1|xk=i),即某时刻颜色 1 的个数为 i 时,其获胜的概率。对应值的计算也很简单,因为 n 个颜色不同的球每个球的获胜概率均为 1n,那么此情况下可以看做有其中 i 个颜色被当作颜色 1,于是 P(F1|xk=i)=in。
采用类似的计算思路,最终我们可以得出:
Pi,i−1|F1=(n−i)×(i−1)n(n−1),Pi,i+1|F1=(n−i)×(i+1)n(n−1),Pi,i|F1=1−(n−i)×2in(n−1)
那么令 fi=E(Ni|F1),将结果代入式 (1),可以得到如下结果:
fi=1+(n−i)×(i−1)n(n−1)fi−1+(n−i)×(i+1)n(n−1)fi+1+(1−(n−i)×2in(n−1))fi
(n−i)×2in(n−1)fi=1+(n−i)×(i−1)n(n−1)fi−1+(n−i)×(i+1)n(n−1)fi+1
2i(n−i)fi=n(n−1)+(n−i)(i−1)fi−1+(n−i)(i+1)fi+1(2)
根据初始条件 fn=0,我们最终可以推出 f1=(n−1)2,具体过程放在最后。
回到本题
令 m=∑ni=1ai ,那么可以得出 P(Fi)=aim。而根据对称性,E(Nai|Fi)=E(Nai|F1)。于是我们就可以得到 ans=∑ni=1fai×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(n−i)fi=n(n−1)+(n−i)(i−1)fi−1+(n−i)(i+1)fi+1
2i⋅fi=n(n−1)n−i+(i−1)fi−1+(i+1)fi+1
令 ti=i⋅fi,对 i∈[1,n) 就有 2ti=n(n−1)n−i+ti−1+ti+1,且 t0=tn=0
那么设 ti=ai⋅ti−1+bi,借由 tn=0 可以推出 an−1=12,bn−1=n(n−1)2。考虑找到 ai,bi 的递推式从而推出 a2,b2 的值,与 i=1 的情况 2t1=n+t2 联立求解
2ti=n(n−1)n−i+ti−1+ti+1=n(n−1)n−i+ti−1+ai+1⋅ti+bi+1
ai=12−ai+1,bi=12−ai+1(bi+1+n(n−1)n−i)
手推几项不难瞪眼看出 ai=n−in−i+1,bi=n−in−i+1⋅n(n−1),于是就有
{2t1=n+t2t2=n−2n−1t1+n(n−2)
最后解得 f1=t1=(n−1)2
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通