题意
给一颗树,从1节点出发,走每条边的概率相同且耗时为1,求每个点第一次被遍历到的期望时间(\(t_1=1\))
思路
在树上只有两种移动方式:从儿子到父亲,从父亲到儿子
假设从\(rt\)转移到\(v\)的期望代价为\(dow_i\),从\(i\)转移到\(rt\)的期望代价为\(val_i\)
假设从\(rt\)转移到\(v\),\(rt\)的度数为\(k\),\(rt\)的父亲为\(fa\),则:
\[dow_v = \frac{1}{k} + \sum_{son}^{son\neq v} { \frac{1}{k}\times (1+val_{son}+dow_v)} + \frac{1}{k}\times (1+dow_{fa}+dow_v)\]
意思是:要么从\(rt\)到\(v\)一步到位,要么有\(\frac{1}{k}\)的概率走其他点再走回来重新计算期望
这里还有个\(val\)不知道,所以要先计算它:
\[val_{rt} = \sum_{son} {\frac{1}{k} + \frac{1}{k} \times (1+val_{son}+val_{rt})}\]
意思是:要么从\(v\)到\(rt\)一步到位,要么走一步到儿子走回来在重新计算期望
一遍\(dfs\)自底向上求\(val\),再一遍\(dfs\)从上到下求\(dow\),答案一个点的答案即为\(dow\)的前缀和
Code
#include<bits/stdc++.h> #define N 100005 #define Min(x,y) ((x)<(y)?(x):(y)) #define Max(x,y) ((x)>(y)?(x):(y)) using namespace std; int n,rd[N],ok=1; double val[N],dow[N],f[N]; struct Edge { int next,to; }edge[N<<1];int head[N],cnt=1; void add_edge(int from,int to) { edge[++cnt].next=head[from]; edge[cnt].to=to; head[from]=cnt; } template <class T> void read(T &x) { char c; int sign=1; while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48; while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign; } void dfs1(int rt,int fa) { val[rt]=rd[rt]; for(int i=head[rt];i;i=edge[i].next) { int v=edge[i].to; if(v==fa) continue; dfs1(v,rt); val[rt]+=val[v]; } } void dfs(int rt,int fa) { double sumval=0; for(int i=head[rt];i;i=edge[i].next) { int v=edge[i].to; if(v==fa) continue; sumval+=val[v]; } for(int i=head[rt];i;i=edge[i].next) { int v=edge[i].to; if(v==fa) continue; f[v]=f[rt]+rd[rt]+dow[rt]+sumval-val[v]; dow[v]=f[v]-f[rt]; dfs(v,rt); } } void solve() { dfs1(1,0); f[1]=1.0; dow[1]=0; dfs(1,0); for(int i=1;i<=n;++i) printf("%.3lf\n",f[i]); } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); read(n); for(int i=1;i<n;++i) { int x,y; read(x);read(y); add_edge(x,y); add_edge(y,x); ++rd[x];++rd[y]; } solve(); return 0; }