LCA 的若干种求法
对于有根树来说,LCA 指两点的最近公共祖先。求法较多,下面选取一些有特色的求法来讲解。
树链剖分法
特点: O ( n ) O(n) O(n) 预处理,单次 O ( log n ) O(\log n) O(logn)。
首先对树进行轻重链剖分预处理。
在求两个点 u , v u,v u,v 的 LCA 时,分为以下两种情况:
- 若 u , v u,v u,v 在同一条重链上,则两者为祖孙关系,返回深度小的点;
- 若 u , v u,v u,v 不在同一条重链上,则可以把更深的那条重链跳过去,LCA 不变。
由于重链数量为 O ( log n ) O(\log n) O(logn),所以单次复杂度为 O ( log n ) O(\log n) O(logn)。
倍增法
特点: O ( n log n ) O(n\log n) O(nlogn) 预处理,单次 O ( log n ) O(\log n) O(logn),常数大,空间大,所以被上者吊打。
改进自逐步上移法。大致分为两个过程:
- 先让深度大的点上移,直到两个深度相等。
- 再让两个点一直向上移动相同步,直到到同一个点。
这两过程均可使用倍增加速。
dfs 序+RMQ 法
原来是打算用欧拉序+RMQ 法的,但好像改用 dfs 序也挺好的,常数更小一点。
特点: O ( n log n ) O(n\log n) O(nlogn) 预处理,单次 O ( 1 ) O(1) O(1)。
首先我们对整棵树进行一次 dfs 得到 dfs 序。假如我们求的是 u , v u,v u,v 的 LCA (其中 d f n u < d f n v dfn_u<dfn_v dfnu<dfnv)。无非两种情况:
-
u
u
u 与
v
v
v 在其 LCA 的两棵子树内。
这种情况下, u , v u,v u,v 的 LCA 的时间戳并不在 [ d f n u , d f n v ] [dfn_u,dfn_v] [dfnu,dfnv] 中。但是, v v v 所在子树离 LCA 最近的节点一定在 [ d f n u , d f n v ] [dfn_u,dfn_v] [dfnu,dfnv] 中,且一定为深度最小点。我们只需要求出 [ d f n u , d f n v ] [dfn_u,dfn_v] [dfnu,dfnv] 中深度最小点,再取其父亲,就是 LCA 了。 - u u u 是 v v v 的祖先。这个时候 LCA 就是 u u u。
我们综合 1 2,只要取出 [ d f n u + 1 , d f n v ] [dfn_u+1,dfn_v] [dfnu+1,dfnv] 中的深度最小点,再取其父亲即可。这个可以用 st 表处理。
Tarjan 法的
还不会