设:
- (d[i]):节点 (i) 的度数
- (fa[i]):节点 (i) 的父亲
我们分为两个部分:儿子到父亲与父亲到儿子。
儿子到父亲
我们先设 (f[i]) 为 (i) 到 (fa[i]) 的期望移动步数。
显然,分为两种情况:
- 一步走到父亲
对于这种情况,只需要走一步即可到达父亲节点,而这种概率为 (frac{1}{d[i]}) ,长度则为 (1) 。那么贡献期望长度为 (frac {1}{d[i]})
- 先走到 (i) 的某个儿子,然后再走回来。
对于这种情况,贡献期望长度当然为:
(走到儿子的步数+儿子走到 (i) 的步数+ (i) 到父亲的步数)/ (d[i])
由于现在走到儿子已经是个钦定的事情,那么步数为 (1) ,而儿子走到 (i) 的步数则必定为 (f[son]) ,(i) 到父亲的步数则一定是 (f[i]) ,是不是看起来重复了 (f[i]) ,挺绕的 ?
那么易得 (f[i]=frac{1+sum (f[son]+f[i]+1)}{d[i]}) ,即是将两个情况的贡献加在一起。由于 (i) 的儿子数肯定为 (d[i]-1) 我们考虑化简这个式子,拆出 (sum) 中的 (1) 与 (f[i]) :
接着我们将右边的 (f[i]) 拆下来移项至左边:
最后即可得到式子:
这个式子很好懂,但是能不能更加简单呢?
由这个式子,我们可以得到每个节点到父亲节点的期望距离。同时我们观察这个式子,它的意思是,当前节点的 (f) 值为所有儿子的 (f) 值之和加上它的度数。那么我们来思考一下:
我们是如何计算 (size) ,即子树大小的?是不是如下的式子:
对于计算 (size) 的这个累加函数,我们可以理解为:每个节点的权值为 (1) ,子树内的权值和。当然,也可以理解为每个节点的大小为 (1) ,子树的大小。
那么我们再仔细端详这个式子:
我们是不是可以理解为:每个节点的权值为 (d[i]) ,子树内的权值之和?
那么这个式子就可以变为:
但是还不够,我们继续化简这个式子,考虑一下对于每条边,它对 (d) 数组的贡献。
(d) 数组为"度数",也就是当前点有多少条边。则我们可以理解为每条边会对两个点的度数有 (1) 的贡献。
那么子树内 (d[i]) 的和,我们就可以理解为计算边的贡献。由于子树内的边必定都连接着子树内的点,则每条边都为子树内的每个点的度数之和增加了 (2) 的贡献。而子树内的边数为 (size[i]-1) ,即每个节点向父亲都有且仅有一条边,那么子树内的边的贡献值和为:((size[i]-1)*2)
那么唯一的例外就只剩下了 (i) 到 (fa[i]) 的这条边,由于 (fa[i]) 并不在子树内,所以它只会对 (i) 产生 (1) 的贡献。那么这个点就不能计算 (2) 的贡献。则加上子树内的贡献,我们可以得到最后的式子:
也可以写成:
父亲到儿子
至于父亲到儿子的话其实挺简单的 (......) 我们考虑就用之前的方法计算,如果以儿子为根重新建树,那么父亲到儿子的期望距离则为 (size'[fa[i]]*2-1) 。
当然不能重新建树 = = 。我们考虑如何得到 (size'[fa[i]]) ,那么就很简单了,(fa[i]) 相对 (i) 的子树,其实就是 (i) 相对 (fa[i]) 的子树以外的所有点。
那么 (size'[fa[i]]=n-size[i]) ,则我们设 (g[i]) 为 (i) 的父亲到 (i) 的期望步数,那就有如下式子:
距离计算
对于给定的 (u->v) 这样一条树上路径,我们可以拆成两条:
(u->lca) 与 (lca->v)
对于第一条路径,肯定是全程向上的,对于第二条路径,肯定是全程向下的。
那么长度肯定为:
则我们可以使用树上前缀和记录每个点到根节点的 (f) 与 (g) 之和,接下来就能够利用前缀和快速求出 (sum) 的值了。