前言
该内容算法竞赛涉及不多,属于较深概率论内容。
鞅的停时定理
“鞅”,martingale 用来指一类随机过程,定义如下:
鞅是一种离散时间的随机过程 满足:
根据定义可以得到 。
停时定理
设 为鞅过程
的停时,当下面三个条件之一成立时,有 :
- 几乎必然有界;
-
一致有界, 有限;
- 一致有界, 几乎必然有限。
名词解释:
- 有限:;
- 有界:;
-
一致有界:;
- 事件 几乎必然发生:。
势能函数
对于随机时间序列 , 为其停时,终止状态为 ,求 。
构造势能函数 ,满足:
构造序列 ,则
,即 是鞅。
根据停时定理,我们可以得到 ,即 。
CF1479E School Clubs
题意
有 个学生和 个组,每个人在一个组里面,第 个组的人数为 。
现在,每天有一个学生(
个学生中随机的一个)会:
- 有一半的概率他会脱离这个组,并且成立一个新的组。
- 有一半的概率他会脱离这个组,并且进入一个已经成立的组。他进入第 个组的概率为 ,他可能会回到原来的组。
求第一次出现所有学生在同一个组的期望天数,对 取模。
思路
设 为状态 的势能,构造使得 ,设 为学生数量为 的组的势能函数,则有:
因为我们需要的只是初始状态与停时的势能差,那么将势能具体设为什么都无所谓,所以为了消去常数项我们可以设
,则:
发现
(表示局面为只有一个人数为
的组)是个常数(知道 和
可以被表示),那么可以用停时定理。
观察数据范围发现没法存下线性求逆元,也没法每次快速幂求。那怎么办呢?
官方题解的做法将
进行差分然后利用快速阶乘算法计算,即:
实际上我们可以直接对对于这个式子
线性递推。因为我们要求的是 和 ,所以我们只在算
个 和
的时候快速幂,递推的时候分别维护分子和分母即可。如果你拥有优秀的常数即可通过。
代码
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; }
|