显然可以树形$dp$ 令$f[i]$表示$i$号节点的期望时间戳 不妨设$fa$有$k$个子节点,对于$i$的子节点$u$,它是第$j(1 \leqslant j \leqslant k)$个被访问的概率是相同的,为$\frac{1}{k}$ 当它作为第$j$个
显然可以树形$dp$
令$f[i]$表示$i$号节点的期望时间戳
不妨设$fa$有$k$个子节点,对于$i$的子节点$u$,它是第$j(1 \leqslant j \leqslant k)$个被访问的概率是相同的,为$\frac{1}{k}$
当它作为第$j$个子节点被访问时,需要从剩下的$k - 1$个节点中挑出$j - 1$个放到它前面,对每种情况的$sz$和取期望
考虑贡献法
一个节点$v$,会给第$j$个被访问的节点的贡献次数为$\binom{k - 2}{j - 2}$
总的贡献次数为$\binom{k - 1}{j - 1}$
因此,第$v$个节点对第$j$个被访问的节点的贡献为$\frac{j - 1}{k - 1} * sz[v]$
也就是说$u$节点在所有情况下需要被耽误的时间应该为$\sum\limits_{1 \leqslant i \leqslant k} \frac{i - 1}{k - 1} * \sum sz[v]$
其中,$v$表示除了$u$以外的所有子节点
化简一番,也就是$f[v] = f[fa] + 1 + \frac{1}{2} * \sum sz[v]$
$O(n)$即可
#include <map> #include <set> #include <queue> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define de double #define le long double #define ri register int #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > ‘9‘ || c < ‘0‘) { if(c == ‘-‘) w = -1; c = gc(); } while(c >= ‘0‘ && c <= ‘9‘) p = p * 10 + c - ‘0‘, c = gc(); return p * w; } } using namespace std; using namespace remoon; #define sid 300050 de f[sid]; int n, sz[sid]; vector <int> son[sid]; inline void dfs(int o, int fa) { sz[o] = 1; for(auto cur : son[o]) dfs(cur, o), sz[o] += sz[cur]; } inline void dfs(int o) { for(auto cur : son[o]) { f[cur] = f[o] + 1 + (sz[o] - sz[cur] - 1) / 2.0; dfs(cur); } } int main() { n = read(); rep(i, 2, n) son[read()].pb(i); dfs(1, 0); f[1] = 1; dfs(1); rep(i, 1, n) printf("%lf ", f[i]); return 0; }