点分治主要是用来处理树上路径问题的工具。
前置知识
树的重心
问题
计算树上所有路径的长度和(用点分治来算)。
算法实现
我们在树上选定一个点 u u u(然后把 u u u 当成根),可以将树上任意两点间的路径分为两种
- 经过 u u u 的,也就是两个点分别在 u u u 的不同子树上。
- 没有经过 u u u 的,也就是两个点在 u u u 的同一个子树内。
这个时候,我们就可以分类计算。
经过 u u u 的
我们设这条经过 u u u 的路径两端分别是为 x x x 和 y y y。
那么我们就是要计算 u u u 到 x x x 的距离再加上 u u u 到 y y y 的距离。
所以,我们可以求出 u u u 到所有剩余节点的距离,然后直接任意两个组合,计算和。
但是,有一些路径组合后是不合法的。
比如说
这样两条路径,组合后是不合法的,因为
x
x
x 和
y
y
y 是同一子树的。
这时候,我们很难再去一一判断是否在同一子树。
我们干脆就直接把工作丢给子树。
设当前计算的子树的根为 v v v,计算一遍每个子树的
∑ x ∈ v 的 子 孙 ∑ y ∈ v 的 子 孙 d i s ( v , x ) + d i s ( v , x ) + 2 × d i s ( u , v ) \sum_{x \in v的子孙}\sum_{y \in v的子孙}dis(v,x) + dis(v,x) + 2\times dis(u,v) x∈v的子孙∑y∈v的子孙∑dis(v,x)+dis(v,x)+2×dis(u,v)
因为任意两个在子树内的节点通往 u u u 的路径组合起来都是不合法的。
我们减去这个和就相当于除去了两个节点都在这个子树内的情况。
这样就做完了。
没有经过 u u u 的
这个很好算,我们会发现,没有经过 u u u 的,一定在 u u u 的一个子树内。
所以我们直接把 u u u 的这个子树拿出来,再跑一遍点分治就行了。
这样就会算出 u u u 的子树的路径长度和。
节点 u u u
这个算法有一个重要的节点 u u u。
如果 u u u 选择的太偏,就会出现一个子树太大,然后影响算法时间复杂度。
所以, u u u 就要选择这棵树的重心。这样是最优的,时间复杂度可以达到 O ( n log n ) O(n\log n) O(nlogn)