小机房的树,LCA,倍增
- 一棵个节点的树,节点编号从到
- 每条边有为的边权
- 求从两个不同的叶节点移动到同一节点最少花费
- 共次询问
思路
如果只有一次询问,可以用最短路算法,然而询问次数过多,使用堆优化迪杰斯特拉在极限数据下计算次数将会达到级别
因为这是一棵树,所以我们可以很容易想到让两个叶子节点在同一高度向上跳,第一次同时跳到的点,维护两个节点跳过的距离就是最小花费
基于这种想法,我们成功引出了LCA——最近公共祖先
做法
求LCA
- 倍增
- ST表
- Tarjan
树链剖分针对我现在是一个NOIP选手的现实基础,我选择使用简单好写的倍增求解法。
倍增求解
朴素算法
朴素LCA算法是先让更深的节点向上跳,跳到和深度较浅的点深度相同时,让两个节点同时向上跳,那么第一个跳到的同一个节点就是这两个节点的最近公共祖先。
可是这样太慢了,极限情况遇到一条链的树就会变成。
如何优化?
既然一次跳一步太少了,那就一次多跳几步
有一个神奇的东西叫做二进制拆分,就是可以把一个数拆成若干个加和的形式,这样每个不大于它的正整数都可以用多项式中若干项的和来表示。
所以,我们用二元组表示从编号为的节点向上跳步可以跳到的节点编号,用表示从编号为的节点向上跳步所需花费(即边权和)。
那么对于每一个与,有
这里用到了倍增的思想,就是从第个点往上跳步相当于从第个点往上跳两个次方步,即先从向上跳步,再从跳到的点向上跳步。
BFS预处理
前面提到了一个操作,就是让两个点先到达同一深度,所以说我们需要用BFS预处理出深度。
设根节点深度为,然后一层一层向下广搜,每次广搜出的点的深度为当前深度加一。
查询
qwq先上代码好了
|
代码
|
为什么说倍增好写又好用呢?因为Tarjan和ST表不能维护瓶颈路径(比如最大边或最小边),而树链剖分又比较难,所以贴心的队长就只讲了倍增qwq。
Author: Sulfur6
Origin: http://sulfur6.github.io
Link: http://sulfur6.github.io/gaogay/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可