「CF696B」Puzzle 題解 (期望與樹形綜合DP)
阿新 • • 發佈:2022-05-12
題目簡介
給一顆樹,按 \(dfs\) 序遍歷並編號,對每個點求其編號的期望值。
分析
鄙人的想法與題解區稍微有點區別(儘管結論一樣,過程卻繁雜得多),希望不被大佬們嫌棄。
令\(f_x\)表示結點 \(x\) 的期望值,\(siz_x\)表示以 \(x\) 為根的子樹大小,考慮當前結點為 \(x\) 怎麼向其子節點 \(y\) 轉移。
令 \(x\) 有 \(k\) 個兒子:
-
假設在 \(x\) 選擇的第一個結點就是 \(y\) ,那麼 \(f_y\) 將等於 \(f_x+1\),而概率是 \(\frac{1}{k}\);
-
假設在 \(x\) 選擇的第一個結點不是 \(y\) ,第二個結點是 \(y\)
-
依此類推,假設 \(y\) 在第三個被選擇,那麼總貢獻將為 \((k-2)\times f_x+2\times \sum siz_{y'}+(k-2)\),相應的概率均為 \(\frac{1}{k-2}\times \frac{1}{k}\)
-
\(\dots\)
綜上,可以得到一個狀態轉移方程:
\[f_{y}=(f_x+1)\times \frac{1}{k}+\overset{k-1}{\underset{i=1}{\sum}}(f_x+\frac{\sum siz_{y'}(y'\ne y)}{n-i}+1)\times \frac{1}{k} \]這麼去推肯定大大的\(\mbox{TLE}\),考慮化簡。
\[\sum siz_{y'}(y'\ne y)=siz_x-siz_y-1 \] \[f_{y}=(f_x+1)\times \frac{1}{k}+\overset{k-1}{\underset{i=1}{\sum}}(f_x+\frac{siz_x-siz_y-1}{n-i}+1)\times \frac{1}{k} \] \[f_{y}=(f_x+1)\times \frac{1}{k}+\overset{k-1}{\underset{i=1}{\sum}}(f_x+\frac{siz_x-siz_y-1}{i}+1)\times \frac{1}{k} \] \[f_{y}=f_x+1+\overset{k-1}{\underset{i=1}{\sum}}\frac{siz_x-siz_y-1}{i}\times \frac{1}{k} \]令 \(t=siz_x-siz_y-1\)
由此得到轉移:
\[f_y=f_x+\frac{siz_x-siz_y-1}{2}+1 \]\(AC\ Code\)
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
const int Maxn=1e5+5;
vector<int>tr[Maxn];
long double f[Maxn];
int siz[Maxn];
void dfs1(int x){
siz[x]=1;
for(auto y:tr[x]){
dfs1(y);
siz[x]+=siz[y];
}
}
void dfs2(int x){
for(auto y:tr[x]){
f[y]=f[x]+1.0*(siz[x]-siz[y]-1)/2+1;
dfs2(y);
}
}
int main(){
int n;cin>>n;
for(int i=2;i<=n;++i){
int x;cin>>x;
tr[x].push_back(i);
}
f[1]=1;
dfs1(1);dfs2(1);
for(int i=1;i<=n;++i)printf("%.7Lf ",f[i]);
return 0;
}