定義
-
概率,就是某個隨機事件出現的可能性大小。
-
若 \(X\) 是一個離散型的隨機變量,可能值為 \(x_1,x_2…\),對應的概率分別為 \(p_1,p_2…\),那么它的期望值為 \(E(x)=\sum_i \limits p_ix_i\)。
期望的線性性
證明:
進而有:
當隨機變量 \(x\) 和 \(y\) 獨立時,有:
條件概率
我們記 \(p(A|B)\) 表示在已知事件 \(B\) 發生時事件 \(A\) 發生的概率,條件概率可以用以下公式計算:
其中 \(p(AB)\) 表示事件 \(B\) 和事件 \(A\) 同時發生的概率,\(p(B)\) 表示事件 \(B\) 發生的概率。
貝葉斯公式
由條件概率的計算方法,我們容易得到貝葉斯公式:
全概率公式
如果隨機變量 \(x\) 有 \(k\) 個取值,分別為 \(x_1, x_2,\ldots ,x_k\),那么:
[CTSC2017]游戲
根據期望的線性性,我們可以單獨計算每個未知的局面的獲勝概率。
設 \(x\) 是一個未知局面,顯然 \(x\) 獲勝的概率只與 \(x\) 左邊的第一個已知局面 \(l\) 和右邊的第一個已知局面 \(r\) 有關。
記事件 \(X\) 為小 \(P\) 在第 \(x\) 局中獲勝,事件 \(L,R\) 為第 \(l,r\) 獲勝者和已知相符。則需要求的是:
我們發現一個區間內的分母是一個定值,因此可以將每個區間內的分母提出,即:
考慮動態更新答案,因此我們每次只需要查詢一個區間的如上式子即可。
分母直接對每個點建立矩陣 \(f_i=\left [\begin{matrix} {1-q_i}&{q_i}\\ {1-p_i}&{p_i}\\ \end{matrix} \right]\) 線段樹維護區間矩陣乘積即可。
考慮維護分子,設矩陣 \(g_i=\left [\begin{matrix} {0}&{q_i}\\ {0}&{p_i}\\ \end{matrix} \right]\) ,發現 \(P(X|L)P(R|X)\) 寫成矩陣形式后就是 \((\prod_{i=l+1}^{x-1}\limits f_i)\times g_x \times (\prod_{i=x+1}^{r}\limits f_i)\)
考慮維護 \(\sum_{x=l+1}^{r-1}\limits [(\prod_{i=l+1}^{x-1}\limits f_i)\times g_x \times (\prod_{i=x+1}^{r}\limits f_i)]\),設 \(F=\prod_{i=l+1}^{r}\limits f_i\),\(G=\sum_{x=l+1}^{r-1}\limits [(\prod_{i=l+1}^{x-1}\limits f_i)\times g_x \times (\prod_{i=x+1}^{r}\limits f_i)]\)。
對於線段樹節點 \(i\),顯然有:
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n,m;
char type[3],op[15];
struct mat{
double a[2][2];
mat(int _x=1){a[0][0]=a[1][1]=_x;a[0][1]=a[1][0]=0;}
inline double* operator [](int t){
return a[t];
}
inline void print(){
printf("%lf %lf\n",a[0][0],a[0][1]);
printf("%lf %lf\n",a[1][0],a[1][1]);
}
inline mat operator *(mat b){
mat res(0);
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
res[i][j]=(res[i][j]+a[i][0]*b[0][j]);
res[i][j]=(res[i][j]+a[i][1]*b[1][j]);
}
}return res;
}
inline mat operator +(mat b){
mat res(0);
for(int i=0;i<2;i++){
for(int j=0;j<2;j++)res[i][j]=a[i][j]+b[i][j];
}return res;
}
}f[800005],g[800005];
double p[200005],q[200005];
void build(int l=0,int r=n,int i=1){
if(l==r){
f[i][0][0]=1-q[l];f[i][0][1]=q[l];f[i][1][0]=1-p[l];f[i][1][1]=p[l];
g[i][0][0]=0;g[i][0][1]=q[l];g[i][1][0]=0;g[i][1][1]=p[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,i<<1);build(mid+1,r,i<<1|1);
f[i]=f[i<<1]*f[i<<1|1];
g[i]=g[i<<1]*f[i<<1|1]+f[i<<1]*g[i<<1|1];
}
mat F,G;
void query(int fr,int to,int l=0,int r=n,int i=1){
if(fr>r||to<l)return ;
if(fr<=l&&to>=r){
G=G*f[i]+F*g[i];F=F*f[i];
return ;
}
int mid=(l+r)>>1;
query(fr,to,l,mid,i<<1);query(fr,to,mid+1,r,i<<1|1);
}
map<int,bool> mp;
inline double calc(int l,int r,bool op1,bool op2){
F=mat(1);G=mat(0);
query(l+1,r);
return G[op1][op2]/F[op1][op2];
}
double ans;
int main(){
scanf("%d%d%s",&n,&m,type);
scanf("%lf",&p[1]);
for(int i=2;i<=n;i++)scanf("%lf%lf",&p[i],&q[i]);
p[0]=1;q[0]=1;++n;
build();mp[0]=1;mp[n]=0;
ans=calc(0,n,1,0);
while(m--){
int i,c;
scanf("%s",op);
if(op[0]=='a'){
scanf("%d%d",&i,&c);
auto r=mp.upper_bound(i),l=r;l--;
ans-=calc(l->first,r->first,l->second,r->second);
ans+=calc(l->first,i,l->second,c);ans+=calc(i,r->first,c,r->second);mp[i]=c;
}
else {
scanf("%d",&i);
auto it=mp.find(i),l=it,r=it;l--;r++;
ans-=calc(l->first,it->first,l->second,it->second);ans-=calc(it->first,r->first,it->second,r->second);
ans+=calc(l->first,r->first,l->second,r->second);mp.erase(it);
}printf("%lf\n",ans);
}
return 0;
}
方差相關
方差等於平方的期望減去期望的平方
矩形覆蓋
給定一個大小為 \(n*m\) 的矩形,某一些格子上有物品,共有 \(k\) 個物品,現在等概率選一個子矩形,求子矩形內物品個數的方差。
考慮 \(Var(x)=E(x^2)-E(x)^2\),分別求 \(E(x)\) 和 \(E(x^2)\)。
\(E(x)\) 較為好求,即統計每一個點的貢獻即可。
求 \(E(x^2)\) 可以考慮對於每一對點,統計包含它們的矩形的個數,考慮分類討論一下它們是左上右下的關系還是右上左下的關系,掃描線計算一下即可。
【UER #6】逃跑
還是考慮求 \(E(x^2)\) 和 \(E(x)\) :
預處理 \(g[t][x][y]\) 表示 \(t\) 時刻從 \((0,0)\) 到達點 \((x,y)\) 的方案數。
設 \(f[i]\) 為 \(i\) 時刻期望經過的的點數,有:
則 :
考慮統計同時經過的點對數的方式求出 \(E(x^2)\) :
設 \(h[t][x][y]\) 表示在 \(t\) 時間內經過了 \((a,b)\) 並在 \(t\) 時刻到達 \((a+x,b+y)\) 的方案數,有:
由於我們統計的是點對,因此 \(\sum_{t=0}^{n}\limits \sum_{x=-n}^{n}\limits \sum_{y=-n}^{n}\limits h[t][x][y]\times (w1+w2+w3+w4)^{n-t}\) 求出的實際上是 \(E(\frac{n\times (n-1)}{2})\) 。
因此 \(E(x^2)=E(x)+2\times \sum_{t=0}^{n}\limits \sum_{x=-n}^{n}\limits \sum_{y=-n}^{n}\limits h[t][x][y]\times (w1+w2+w3+w4)^{n-t}\) 。
題目要求 \(Var(x)\times (w1+w2+w3+w4)^n\) ,而 \(\text{std}\) 求的是 \(Var(x)\times (w1+w2+w3+w4)^{2\times n}\)。
因此最終輸出應該輸出 \(E(x^2)\times (w1+w2+w3+w4)^n-E(x)^2\) 。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n;
long long w1,w2,w3,w4,sum;
const long long md=998244353;
int g[105][205][205],h[105][205][205],f[105],pwr[105];
long long E1,E2;
int main(){
scanf("%d",&n);
scanf("%lld%lld%lld%lld",&w1,&w2,&w3,&w4);sum=(w1+w2+w3+w4)%md;
g[0][n][n]=1;
for(int t=0;t<n;t++){
for(int i=0;i<=2*n;i++){
for(int j=0;j<=2*n;j++){
if(!g[t][i][j])continue;
// cout<<t<<" "<<i<<" "<<j<<" "<<g[t][i][j]<<endl;
g[t+1][i-1][j]=(g[t+1][i-1][j]+w1*g[t][i][j])%md;
g[t+1][i+1][j]=(g[t+1][i+1][j]+w2*g[t][i][j])%md;
g[t+1][i][j-1]=(g[t+1][i][j-1]+w3*g[t][i][j])%md;
g[t+1][i][j+1]=(g[t+1][i][j+1]+w4*g[t][i][j])%md;
}
}
}
pwr[0]=1;
for(int i=1;i<=n;i++)pwr[i]=pwr[i-1]*sum%md;
for(int i=0;i<=n;i++){
f[i]=pwr[i];
for(int j=1;j<=i;j++)f[i]=(f[i]-1ll*f[i-j]*g[j][n][n])%md;
// cout<<i<<" "<<f[i]<<endl;
E1=(E1+1ll*f[i]*pwr[n-i])%md;
}E2=E1;
for(int t=0;t<=n;t++){
for(int i=0;i<=2*n;i++){
for(int j=0;j<=2*n;j++){
if(i==n&&j==n)continue;
for(int k=0;k<t;k++)h[t][i][j]=(h[t][i][j]+1ll*f[k]*g[t-k][i][j]-1ll*h[k][2*n-i][2*n-j]*g[t-k][i][j]-1ll*h[k][i][j]*g[t-k][n][n])%md;
E2=(E2+2ll*h[t][i][j]*pwr[n-t])%md;
}
}
}
printf("%lld",((E2*pwr[n]-E1*E1)%md+md)%md);
return 0;
}
概率生成函數
對於任意取值在非負整數集上的離散隨機變量 \(x\),它的概率生成函數為:
概率生成函數的一些性質
1.
2.
3.
4.
[CTSC2006]歌唱王國
設 \(f[i]\) 表示第 \(i\) 輪結束的概率,\(g[i]\) 表示到了第 \(i\) 輪還沒有的概率。
設生成函數:
顯然:
有:
我們考慮在一個長度為 \(i\) 且沒有結束的序列后面生成一個目標序列,直接在序列后面生成的概率是 \(g[i]\times (\frac{1}{n})^m\) 。
假如目標序列的 \(a_{1\to j}=a_{m-j+1\to m}\),那么我們在長度為 \(i+j\) 且結束了的序列后面插入 \(m-j\) 個字符也可以生成相同的序列,概率為 \(\sum_{j=1}^{m}\limits [a_{1\to j}=a_{m-j+1\to m}]f[i+j]\times (\frac{1}{n})^{m-j}\)
記 \(kmp[i]=[a_{1\to i}=a_{m-i+1\to m}]\) 。
有:
進而:
對 \(F(x)=x\cdot G(x)+G(x)\) 求導可得:
令 \(x=1\),有:
聯合第二個方程,有:
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n,t,m;
int a[100005],nxt[100005],pwr[100005];
const int md=10000;
int main(){
scanf("%d%d",&n,&t);
pwr[0]=1;
for(int i=1;i<=1e5;i++)pwr[i]=pwr[i-1]*n%md;
while(t--){
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=2,j=0;i<=m;i++){
while(j&&a[i]!=a[j+1])j=nxt[j];
if(a[i]==a[j+1])j++;
nxt[i]=j;
}int ans=0;
for(int i=m;i;i=nxt[i])ans=(ans+pwr[i])%md;
if(ans<1000)printf("0");
if(ans<100)printf("0");
if(ans<10)printf("0");
printf("%d\n",ans);
}
return 0;
}
Dice
有一個 \(m\) 面的骰子,求扔連續 \(n\) 次相同就結束的期望步數和扔連續 \(n\) 次結果不同就結束的期望步數。
對於第一問,可以列出:
有:
解得:
第二問:
有:
解得:
例題
CF1187F Expected Square Beauty
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n;
int l[200005],r[200005];
const long long md=1e9+7;
long long R[200005],pre[200005],suf[200005];
inline long long pwr(long long x,long long y){
long long res=1;
while(y){
if(y&1)res=res*x%md;
x=x*x%md;y>>=1;
}return res;
}
long long ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&l[i]);
for(int i=1;i<=n;i++)scanf("%d",&r[i]);
for(int i=0;i<n;i++){
int len=max(0,min(r[i],r[i+1])-max(l[i],l[i+1])+1);
R[i]=(1-len*pwr(r[i]-l[i]+1,md-2)%md*pwr(r[i+1]-l[i+1]+1,md-2))%md;
}
for(int i=0;i<n;i++)pre[i]=(pre[i-1]+R[i])%md;
for(int i=n-1;~i;i--)suf[i]=(suf[i+1]+R[i])%md;
for(int i=2;i<n;i++)ans=(ans+R[i]*pre[i-2])%md;
for(int i=0;i<n;i++)ans=(ans+R[i]*suf[i+2])%md;
for(int i=0;i<n;i++)ans=(ans+R[i])%md;
for(int i=0;i+1<n;i++){
int len=max(0,min(r[i],min(r[i+1],r[i+2]))-max(l[i],max(l[i+1],l[i+2]))+1);
ans=(ans+2*(R[i]+R[i+1]-1+len*pwr(r[i]-l[i]+1,md-2)%md*pwr(r[i+1]-l[i+1]+1,md-2)%md*pwr(r[i+2]-l[i+2]+1,md-2)%md)%md)%md;
}
printf("%lld",(ans+md)%md);
return 0;
}