LCA 问题解法(Least Common Ancestor)

LCA = Least Common Ancestor

1. 倍增

先确定一点为根。
预处理出\(f[i][j]\), 表示从\(i\)开始往上跳\(2^j\),所到达的节点编号。

对于求解\(a\)和\(b\)的LCA:(假设其LCA为节点\(c\), \(c\)的儿子节点为\(k\))
我们先让它们跳到同一高度。
然后应注意这几个事实:\(a\)和\(b\)到\(k\)所需要跳的长度\(len\)是相等的。也就是说存在一个唯一的\(len\)的二进制分解;\(2^i>2^{i-1}+2^{i-2}+...+2^0\)
接下来要做的是从大到小枚举\(2\)的次幂,对于\(2^i\), 如果\(a\)和\(b\)往上跳\(2^i\)不相等,那就让\(a\)和\(b\)往上跳\(2^i\),即\(len\)的二进制分解后第\(i\)位为\(1\)

2. 将树上 LCA 转化为序列 RMQ(Range Minimum/Maximum Query)

2.0 定义

欧拉序列:上面这棵树所对应的欧拉序列为:1, 5, 1, 4, 3, 4, 2, 4, 1
dfs 序:1 5 4 3 2。后文用 \(D_i\) 代替 dfs 序中的第 \(i\) 项。
dfn:\(dfn_1=1,dfn_5=2,dfn_4=3,dfn_3=4,dfn_2=5\)
\(pos_i\) : 节点 \(i\) 在欧拉序列中第一次出现的位置。如,\(pos_4=4\)。
\(LCA(a, b)\):\(a, b\) 的 LCA 的节点编号。

2.1 dfs 序

  • 一个事实:遍历以 \(x\) 为根的子树:\(x\) — \(x\) 的子树1 — \(x\) 的子树2... || 在 dfs 序中,\(x\) 的后代一定在 \(x\) 之后。

  • 算法分析

问题:对于 \(u, v\), 求 \(LCA(u,v)\)。\(u=v\) 特判。

不妨设 \(dfn_u<dfn_v\),即先遍历到 \(u\)。

① 当 \(u,v\) 不为祖孙关系时,即 \(LCA(u,v)\neq u\)
\(D[dfn[u]...dfn[v]]\) 中/深度最小的节点/的父亲是 \(LCA(u,v)\)。(※)

② 若 \(u\) 是 \(v\) 的祖先,我们想要沿用 ① 的方法(※),希望它能推广到这种情况。
\(D[dfn[u]+1...dfn[v]]\) 中/深度最小的节点/的父亲是 \(LCA(u,v)\)。
事实上,我们发现这个对于 ① 的情况也适用。(※)

综上,对于 \(dfn_u<dfn_v\), \(D[dfn[u]+1...dfn[v]]\) 中/深度最小的节点/的父亲是 \(LCA(u,v)\)。

2.2 欧拉序

  • 算法过程:

对于给定的有根树 \(T\), 求出其欧拉序列 \(F\)。
现在想要求节点 \(a, b\) 的 LCA。
不妨设 \(pos_a<=pos_b\)。
有结论: 设 \(F_k\) 的深度是 \(F[pos_a...pos_b]\) 中深度最小的节点,节点 \(a, b\) 的 LCA 就是 \(F_k\)。

  • 正确性说明:

首先,\(F[pos_a...pos_b]\) 中有 \(LCA(a, b)\)。
其次,\(F[pos_a...pos_b]\) 中深度最小的节点有且仅有一个。
最后,\(LCA(a,b)\) 是 \(F[pos_a...pos_b]\) 中深度最小的节点。

posted @ 2023-02-03 15:23  Jiayn  阅读(94)  评论(0)    收藏  举报