- 機率DP是DP中一個非常重要且較難的DP型別。其題型靈活多變,尤其愛與樹形DP結合,同時很可能需要各種資料結構最佳化。
- 其主要考點便是DP方程的建立與維護。由於“機率”二字,許多時候分類討論與小數運算也是不可避免的。
因此,機率DP對選手的邏輯思維與程式碼能力也有很高的要求,可以說是DP中的集大成者。
P2081 [NOI2012] 迷失遊樂園
- 題意:給定一顆有 \(n\) 個點的樹或基環樹,每條邊有邊權 \(w_i\)
求從每個點開始,在樹上隨機不重地走,最後的期望經過的邊權和。
對於每個節點,其下一步走到其任意相鄰點的機率是相同的。 - 資料範圍 \(n\le 1e5,w_i \le 100\)
普通樹
先考慮在普通的樹上怎麼做。假設我們先欽定 \(rt\) 為根節點,那麼對於每個除根以外的節點,其第一步的走法有兩種:
- 向父親走
- 向兒子走
容易發現,如果我們已經求出這個點所有兒子再向下走的機率,那這個點向兒子走的機率轉移是樸素的。
設第一步向下走的期望權值為 \(down_u\),則有
\[{\Large down_u=\frac{\sum_vdown_v+w_{u,v}}{son_u}}
\]
- 其中 \(son_u\) 代表 \(u\) 的兒子個數,\(v\) 代表 \(u\) 的某一個兒子。
而向父親走的情況稍微複雜了一點。由於這個點走向父親後還能再向其其他兒子走,也可以繼續向上,因此情況要考慮完全
設 \(u\) 第一步向上的期望權值 為 \(up_u\),則有
\[{\Large up_u=w_{u,k}+\frac{up_k\cdot fa_k+down_k\cdot son_k-down_u-w_{u,k}}{son_k+fa_k-1}}
\]
- 其中 \(k\) 是 \(u\) 的父親,\(fa_k\) 是 \(k\) 父親的數量。這聽起來可能有些奇怪,因為普通樹只有一個或沒有父親。
不過在一會要討論的基環樹中,\(fa_u\) 就能體現出作用 - \(up_k\cdot fa_k\) 是繼續向上走的貢獻,\(down_k\cdot son_k\) 是 \(k\) 又向下走的貢獻。
但因為不能重複走到 \(u\) 點,因此需要減去貢獻。注意上面是總的貢獻,因此可以直接減
最後,因為不能走回 \(u\),因此總共有 \(son_k+fa_k-1\) 種情況。 - 由於 \(up_u\) 需要由 \(down_u\) 與 \(down_k\) 推出,因此對於普通樹,先求 \(down\) 再求 \(up\) 即可。
但需要注意根節點沒有父親,因此處理 \(up\) 時,注意從根節點的所有兒子開始處理。
點選檢視程式碼
void make_down(int u,int k)
{
for(int i=0;i<tot[u];i++)
{
int v=q[u][i].v;if(vis[v]||v==k) continue;
fa[v]=1;make_down(v,u);son[u]++;down[u]+=1.0*(down[v]+q[u][i].w);
}
if(son[u]) down[u]=down[u]/son[u];
}
void make_up(int u,int k,ld w)
{
up[u]=w;
if(fa[k]+son[k]-1)
up[u]=up[u]+(up[k]*fa[k]+down[k]*son[k]-down[u]-w)/(son[k]+fa[k]-1);
for(int i=0;i<tot[u];i++){
int v=q[u][i].v;if(v==k||vis[v]) continue;
make_up(v,u,q[u][i].w);
}
}
void work1()
{
make_down(1,0);
for(int i=0;i<tot[1];i++) make_up(q[1][i].v,1,q[1][i].w);
}
基環樹
- 可以先畫個圖。
- 紅邊是環上的邊。可以發現,如果我們將紅邊刪掉,就是一片森林。因此對