Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

前言

该内容算法竞赛涉及不多,属于较深概率论内容。

鞅的停时定理

“鞅”,martingale 用来指一类随机过程,定义如下:

鞅是一种离散时间的随机过程 X0,X1, 满足:

  • E(Xt)<,t0
  • E(Xt+1X0,Xt)=Xt

根据定义可以得到 E(Xt)=X0

停时定理

t 为鞅过程 X0,X1, 的停时,当下面三个条件之一成立时,有 E(Xt)=X0

  • t 几乎必然有界;
  • Xi+1Xi 一致有界,E(t) 有限;
  • Xi 一致有界,t 几乎必然有限。

名词解释:

  • aR 有限:a∣<
  • aR 有界:l,rR,a[l,r]
  • aiR 一致有界:i,l,rR,ai[l,r]
  • 事件 A 几乎必然发生:P(A)=1

势能函数

对于随机时间序列 A0,A1,t 为其停时,终止状态为 At,求 E(t)

构造势能函数 Φ(A),满足:

  • E(Φ(An+1)Φ(An)A0,A1,,An)=1
  • Φ(At) 为常数。

构造序列 Xi=Φ(Ai)+i,则 E(Xn+1XnX0,X1,,Xn)=0,即 X0,X1, 是鞅。

根据停时定理,我们可以得到 E(Xt)=E(X0),即 E(t)=E(Φ(A0))Φ(At)

CF1479E School Clubs

题意

n 个学生和 m 个组,每个人在一个组里面,第 i 个组的人数为 ai

现在,每天有一个学生(n 个学生中随机的一个)会:

  1. 有一半的概率他会脱离这个组,并且成立一个新的组。
  2. 有一半的概率他会脱离这个组,并且进入一个已经成立的组。他进入第 i 个组的概率为 ain,他可能会回到原来的组。

求第一次出现所有学生在同一个组的期望天数,对 998244353 取模。

n4×108

思路

ϕ(A) 为状态 A 的势能,构造使得 ϕ(At)1=ϕ(At+1),设 f(x) 为学生数量为 x 的组的势能函数,则有: ϕ(At)1=ϕ(At+1)ϕ(At)1=iain(12(ϕ(At)f(ai)+f(ai1)+f(1))+ai2nϕ(At)+jiaj2n(ϕ(At)f(ai)f(aj)+f(ai1)+f(aj+1)))1=iai2n(f(ai)+f(ai1)+f(1)+jiajn(f(ai)f(aj)+f(ai1)+f(aj+1)))1=iai2nf(ai)+iai2nf(ai1)+iai2nf(1)iai2njiajnf(ai)iai2njiajnf(aj)+iai2njiajnf(ai1)+iai2njiajnf(aj+1)iain=iai2nf(1)i3nai2ai22n2f(ai)+i2naiai22n2f(ai1)+inaiai22n2f(ai+1)xn=x2nf(1)3nx2x22n2f(x)+2nxx22n2f(x1)+nxx22n2f(x+1)

因为我们需要的只是初始状态与停时的势能差,那么将势能具体设为什么都无所谓,所以为了消去常数项我们可以设 f(1)=2,则: 0=3nx2x22n2f(x)+2nxx22n2f(x1)+nxx22n2f(x+1)f(x+1)=2n2nxx2(3nx2x22n2f(x)2nxx22n2f(x1))f(x+1)=3n2xnxf(x)2nxnxf(x1)

发现 ϕ(At)=f(n) (表示局面为只有一个人数为 n 的组)是个常数(知道 f(0)f(1) 可以被表示),那么可以用停时定理。

观察数据范围发现没法存下线性求逆元,也没法每次快速幂求。那怎么办呢?

官方题解的做法将 f 进行差分然后利用快速阶乘算法计算,即: g(x)=f(x+1)f(x)g(x)=2nxnxg(x1)g(x)=g(0)i=1x2ninif(x)=2i=0x1j=1i2njnj

实际上我们可以直接对对于这个式子 f(x+1)=3n2xnxf(x)2nxnxf(x1) 线性递推。因为我们要求的是 E(ϕ(At))ϕ(A0)=if(ai),所以我们只在算 mf(ai)f(n) 的时候快速幂,递推的时候分别维护分子和分母即可。如果你拥有优秀的常数即可通过。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<cstdio>
#include<algorithm>
inline int read(){
int x=0,w=0;char c=getchar();
while(!isdigit(c)) w|=c=='-',c=getchar();
while(isdigit(c)) x=x*10+(c^48),c=getchar();
return w?-x:x;
}
const int mod=998244353;
inline int pow(int a,int b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod; return ans;}
int n,m,a[1010];
signed main(){
m=read();
for(int i=1;i<=m;i++) n+=a[i]=read();
std::sort(a+1,a+m+1);
int ans=0;
int s1=0,d1=mod-2,s2=1,d2=1,p1,p2,j=1;
while(j<=m&&a[j]==1)ans=(ans+mod-2)%mod,++j;
for(int i=1;i<n;i++){
p2=1ll*(n-i)*d2%mod*s2%mod;
p1=((3ll*n-2*i)*d1%mod*s2+(mod-1ll)*(2ll*n-i)%mod*s1%mod*d2)%mod;
while(j<=m&&a[j]==i+1)ans=(ans+1ll*p1*pow(p2,mod-2))%mod,++j;
s1=d1,s2=d2,d1=p1,d2=p2;
}
printf("%lld\n",(ans+(mod-1ll)*d1%mod*pow(d2,mod-2))%mod);
return 0;
}

给小狼留言