算法描述
根据上一个博客介绍的dfs序以及欧拉序能够把树上的点转为线性的区间点,从而可以用区间的数据结构去维护。根据欧拉序的定义,我们会发现树上任意两点的第一次出现位置之间必然夹带着lca的点,至于为什么可以画图理解一下,因为我们生成这个欧拉序时每次回溯就加一个点,而任意两点之间的搜索树一定是从lca开始往下搜,然后回溯再转而去搜另外一个点,所以lca就生成再两点的时间戳之间了。
于是我们维护完欧拉序后我们可以得到序列中深度最小的那个点必然是lca,这两点之间不会再夹带深度更小的点了,原因和上述蓝字一致。至此,整个问题从树上求LCA转为求区间序列深度最小的点,即RMQ问题,对于这个算法有个 O ( n l o g n ) O(nlogn) O(nlogn)预处理, O ( 1 ) O(1) O(1)查询的高效算法:ST表(基于动态规划和倍增思想)。这份博客有关于ST表求RMQ的讲解:戳这里。
不过我们的状态要重新设计一下,设 s t [ i ] [ j ] st[i][j] st[i][j]表示起点为i,跳 2 j 2^j 2j步长的深度最小的点,这样设计的原因是我们要维护的是深度最小值,但要求的是最小值的那个点,不这样子还要多一次哈希,感觉没啥必要,直接找这个点,状态转移的时候哈希到深度就可以了。(其实就是少了n长度的空间)。其他的和st表的基本操作一致。
实现
#include <bits/stdc++.h>
using namespace std;
const int maxnn = (int)5e5+5;
const int maxnm = (int)1e6+5;
/**
* 利用欧拉序中两点的lca会包含在两个点的in之中的性质
* 查询lca(u, v)相当于查min(