题目大意: 给出一棵树,每个点有两个属性 a , b a,b a,b,设 A = ∑ a , B = ∑ b A=\sum a,B=\sum b A=∑a,B=∑b,那么 a A \dfrac a A Aa 就是这个点成为起点的概率, b B \dfrac b B Bb 是这个点成为终点的概率。随机选择一个起点后,这个起点将成为树根,每次只能向下走,到达一个点后,会将这个点的所有儿子随机排序然后依次遍历,走完所有儿子后会回到父亲,走到终点就停止,问起点走到终点期望需要多少步。
题解
这题描述的挺绕的……不过勉强能理解吧qwq
对于一个点 x x x,假如走错了,走到了儿子 y y y,而终点不在 y y y 的子树中,那么就额外走了 2 × s i z e [ y ] 2\times size[y] 2×size[y] 步,假设终点所在子树为儿子 z z z,那么在 x x x 的儿子的排序中, y y y 有 1 2 \frac 1 2 21 的概率在 z z z 前面,所以每一个儿子被走错的概率都是 1 2 \frac 1 2 21,所以每个儿子提供的期望步数都是 s i z e [ y ] size[y] size[y]。
那么考虑每一条边 ( x , y ) (x,y) (x,y),假设 x x x 作为终点,起点在 y y y 的子树中,可以发现,无论起点是谁,总期望步数都是 s i z e [ y ] size[y] size[y](随便画个图走一走就明白了),乘上起点在 y y y 内的概率以及 x x x 成为起点的概率就是这条边的贡献了,全部加起来就是答案。
代码如下:
#include <cstdio>
#define maxn 100010
int n;
struct edge{int y,next;};
edge e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y)
{
e[++len]=(edge){y,first[x]};
first[x]=len;
}
double st[maxn],ed[maxn],tot_st=0,tot_ed=0,val[maxn],ans=0;
int size[maxn];
void dfs(int x,int fa)
{
size[x]=1;val[x]=st[x];
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y; if(y==fa)continue;
dfs(y,x);size[x]+=size[y];val[x]+=val[y];
}
}
int main()
{
scanf("%d",&n);
for(int i=1,x,y;i<n;i++)
scanf("%d %d",&x,&y),buildroad(x,y),buildroad(y,x);
for(int i=1;i<=n;i++)
scanf("%lf %lf",&st[i],&ed[i]),tot_st+=st[i],tot_ed+=ed[i];
for(int i=1;i<=n;i++)st[i]/=tot_st,ed[i]/=tot_ed;
dfs(1,0);
for(int x=1;x<=n;x++)
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(size[y]<size[x])ans+=val[y]*ed[x]*size[y];
else ans+=(1-val[x])*ed[x]*(n-size[x]);
}
printf("%.10lf",ans);
}