鞅
鞅最早指一種賭博策略,后被引進到了數學中,用來指一類隨機過程。它有許多種不同程度的推廣,這里義離散時間鞅為滿足以下條件的隨機過程(依賴於時間的隨機變量序列) \(X_0,X_1,X_2,…\) 。
- \(∀n∈N,E\ [X_n]<∞\)。
- \(∀n∈N+,E\ [X_{n+1}∣X_n,X_{n−1},…,X_0]=X_n\)。
第一條比較平凡,第二條用比較通俗的語言講,就是在已知之前的所有觀測值的前提下,下一次觀測值的期望等於這一次觀測值。
同時有更為一般的定義,若:
- \(∀n∈N,E\ [Y_n]<∞\)。
- \(∀n∈N+,E\ [Y_{n+1}∣X_n,X_{n−1},…,X_0]=Y_n\) 。
則稱隨機過程 \(Y0,Y1,…\) 是關於另一隨機過程 \(X0,X1,…\) 的鞅。
容易證明,對於任意 \(n∈N+,E\ [X_n]=X_0\),也就是認為 \(X_0,X_1,…\) 是賭徒的財產,則無論賭多少次,賭徒最后的期望財產依舊等於他最開始的財產。
下面舉幾個鞅的例子:
-
設 \(X_n\) 表示一個賭徒拋擲了 \(n\) 次公平硬幣(正反面概率相等)后的財產。規則是若為正面朝上,則獲得 \(1\) 元;反之輸掉 \(1\) 元。在已知過去所有時刻的財產下,若賭徒再拋擲一次硬幣,顯然拋擲完后財產的期望依舊等於現在的財產數量。故 \(X_0,X_1,X_2,…\) 這一隨機過程是鞅。
-
接着上面的例子,設 \(Y_n=X^2_n−n\),則 \(Y_0,Y_1,…\) 這一隨機過程是關於隨機過程 \(X_0,X_1,…\) 的鞅,證明如下:
注意到:
取 \(X_0=0\) 則 \(E\ [X^2_n]=n\) ,如果 \(X_0≠0\) 只需要 \(Y_n\) 定義為 \((X_n−X_0)^2−n\) 即可。
這一例子可以表明賭徒的全部收益或損失大致在拋擲次數的正負平方根之間變化。
鞅的停時定理
對於一列隨機變量 \(X_0,X_1,…\),停時 \(τ\) 為一個隨機變量,滿足性質:對於任意時間 \(t\),事件 \(τ=t\) 是否發生僅取決於 \(X_0,X_1,…\) 的取值。也就是說只根據現在獲得的信息就可以知道是否已經停下,而不需要將來的信息。
隨機時刻:
設隨機變量 \(T\) , 如果 \(T∈N∪{+∞}\) , 且 \(T=n\) 的示性函數 \(I_{T=n}\) 為 \(X_0,X_1...,X_n\) 的函數,則稱 \(T\) 為 \(X\) 的隨機時刻.
停時 :
若 \(T\) 為 \(X\) 的隨機時刻且 \(P(T<+∞)=1\) 則稱 \(T\) 為 \(X\) 的停時。
停止過程:
若 \(T\) 為 \(X\) 的隨機時刻。定義 \(X\) 的停止過程 \(\overline{X}\) 為
顯然若 \(X\) 為鞅則 \(\overline{X}\) 為關於 \(X\) 的鞅。
停時定理:
若鞅 \(X\) 和停時 \(T\) 滿足以下條件之一:
- \(\overline{X}\) 都有界。
- \(T\) 有界。
- \(E\ (T)<+∞\) , 且 \(E\ (|X_n+1−X_n||X_0,...,X_n)\) 有界。
那么 \(E\ (X_T)=E\ (X_0)\)
套路:
構造勢函數 \(ϕ(X)\) , 使得 \(E(ϕ(X_{n+1})|X_0,...,X_n)=ϕ(X_n)+1\)。
這樣的話 \(ϕ(X_n)−n\) 就是一個鞅。
利用停時定理有:
即:
這樣求出 \(E(ϕ(X_0))\) 和 \(E(ϕ(X_T))\) 就可以求出 \(E(T)\) 了。
例題
#88. 戰爭
我們設 \(ϕ(X)=\sum f(a_{i})\) ,列方程:
假如發生沖突的是 \(i\) 國家和 \(j\) 國家:
令 \(f(1)=0\) 式子會簡潔很多
我們只需讓 \(∀a\),有 \(p\times f(a+1)-f(a)=\frac{1}{2}\) 即可。
對於 \(f(n)=k\times f(n-1)+b\) 形式的遞歸式,可以設 \(f(n)+c=k\times (f(n-1)+c)\) ,解出 \(c\) ,剩余的部分是一個等比數列。
本題中 \(c=\frac{1}{2(p-1)}\) ,進而 \(f(n)=(p^{1-n}+1)\times \frac{1}{2(p-1)}\) 。
因此我們可以方便的計算 \(E(X_T)\) 和 \(E(X_0)\) ,輸出 \(E(X_T)-E(X_i)\) 即可。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int p=1e9+7,inv2=(p+1)/2;
int a[40000005],pw0[65537],pw1[65537];
int pw(int x,int y){
int res=1;
while(y){
if(y&1)res=1ll*res*x%p;
x=1ll*x*x%p;y>>=1;
}
return res;
}
int mod(int x){return x>=p?x-p:x;}
unsigned long long x,y,z,b1,b2;
void prework(){
int m,q=0,now=1;unsigned long long l,r,tmp;
scanf("%d%llu%llu%llu%llu%llu",&m,&x,&y,&z,&b1,&b2);
while(m--){
scanf("%d%llu%llu",&q,&l,&r);
r=r-l+1;
while(now<=q)a[now]=(b1%r+l)%(p-1),tmp=x*b2+y*b1+z,b1=b2,b2=tmp,now++;
}
}
int pw2(int x){return 1ll*pw0[x&65535]*pw1[x>>16]%p;}
int main(){
int type,n,s,t,r,x0=0;long long x;
scanf("%d%d%d%d",&type,&n,&s,&t);
t=1ll*s*pw(t,p-2)%p;r=pw(mod(2*t-2),p-2);
if(!type)for(int i=1;i<=n;i++)scanf("%lld",&x),a[i]=x%(p-1);
else prework();
s=0;
int ti=pw(t,p-2);
pw0[0]=pw1[0]=1;
for(int i=1;i<=65536;i++)pw0[i]=1ll*pw0[i-1]*ti%p;
for(int i=1;i<=65535;i++)pw1[i]=1ll*pw1[i-1]*pw0[65536]%p;
for(int i=1;i<=n;i++){
s=(s+a[i])%(p-1);
x0=mod(x0+pw2(a[i]+p-2)-1);
}
printf("%lld",1ll*(x0-pw(t,p-s)+1+p)*r%p);
return 0;
}
CF1025G Company Acquisitions
設 \(f(x)\) 為被選中點后面跟隨 \(x\) 個未被選中點的勢能,設 \(ϕ(A)\) 表示整個局面的勢能。
列方程:
設被選中的兩個點后面分別跟隨着的點的數量為 \(a\) 和 \(b\) ,有:
令 \(f(0)=0\) 有:
不妨令 :
也就是:
令初始時節點 \(i\) 后面跟着的節點數為 \(a_i\) ,有:
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int md=1e9+7;
inline int pwr(int x,int y){
int res=1;
while(y){
if(y&1)res=1ll*res*x%md;
x=1ll*x*x%md;y>>=1;
}
return res;
}
int n,a[505];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
a[x]++;
}
int res=(pwr(2,n-1)-1+md)%md;
for(int i=1;i<=n;i++){
if(a[i])res=(res-pwr(2,a[i])+1+md)%md;
}
printf("%d\n",res);
return 0;
}
CF850F Rainbow Balls
考慮枚舉一下球的顏色,將所有球分成兩類,是這種顏色的與不是這種顏色的,設 \(f(x)\) 為有 \(x\) 個這種顏色的球的勢能,設 \(ϕ(A)\) 表示整個局面的勢能。
令 \(m=\sum_{i=1}^n a_i\) ,有:
化簡:
由於 \(\sum_{i=1}^n\frac{a_i}{m}=1\) ,我們想讓上式成立只需:
也就是:
令 \(g(i)=f(i)-f(i-1)\) ,有:
我們可以遞推出 \(g(i)\),然后遞推出 \(f(i)\),但是直接遞推出終狀態的勢能是 \(O(m)\) 的,我們看能不能快速計算:
時間復雜度 \(O(\max a_i\log n)\) 。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n,m,lim;
int a[2505],g[100005],f[100005];
const int md=1e9+7;
inline int pwr(int x,int y){
int res=1;
while(y){
if(y&1)res=1ll*res*x%md;
x=1ll*x*x%md;y>>=1;
}
return res;
}
inline void init(){
for(int i=1;i<=lim;i++)g[i]=(g[i-1]+1ll*(m-1)*pwr(m-i+1,md-2))%md;
for(int i=1;i<=lim;i++)f[i]=(f[i-1]+g[i])%md;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
lim=max(lim,a[i]);m=(m+a[i])%md;
}
init();
int res=1ll*m*(m-1)%md;
for(int i=1;i<=n;i++)res=(res-f[a[i]]+md)%md;
printf("%d\n",res);
return 0;
}
CF1479E School Clubs
類似上一題列式子的方法,可以推出:
令 \(f(1)=−2\),可以消去常數項,然后寫成遞推式的形式:
這樣就可以 \(O(n)\) 地解決了,線性算即可,可以過 。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int const md=998244353;
int pwr(int x,int y){
int res=1;
while(y){
if(y&1)res=1ll*res*x%md;
x=1ll*x*x%md,y>>=1;
}
return res;
}
int n,m,a[1005];
int main(){
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",a+i),n+=a[i];
sort(a+1,a+m+1);
int ans=0;
int s1=0,d1=md-2,s2=1,d2=1,p1,p2,j=1;
while(j<=m&&a[j]==1)ans=(ans+md-2)%md,++j;
for(int i=1;i<n;i++){
p2=1ll*(n-i)*d2%md*s2%md;
p1=((3ll*n-2*i)*d1%md*s2+(md-1ll)*(2*n-i)%md*s1%md*d2)%md;
while(j<=m&&a[j]==i+1)ans=(ans+1ll*p1*pwr(p2,md-2))%md,++j;
s1=d1,s2=d2,d1=p1,d2=p2;
}
printf("%lld\n",(ans+(md-1ll)*d1%md*pwr(d2,md-2))%md);
return 0;
}