基于树上二分倍增(Binary Lifting)大步翻倍跃升,实现 $O(\log N)$ 级别两点公共祖先定位
在有根树中,两个节点 $u$ 和 $v$ 的最近公共祖先 (LCA) 是指深度最大且同时是 $u$ 和 $v$ 祖先的节点。朴素的逐层向上查找复杂度为 $O(N)$,倍增算法通过翻倍跳跃可将单次查询优化至 $O(\log N)$。
我们用 fa[u][j] 表示节点 $u$ 向上跳跃 $2^j$ 步到达的祖先。其核心状态转移方程为:
其物理意义为:向上跳跃 $2^j$ 步,等价于先向上跳跃 $2^{j-1}$ 步,再从该祖先向上跳跃 $2^{j-1}$ 步。(例如:向上跳 8 步等于先跳 4 步,再跳 4 步)。
fa[u][0] 即为最终的 LCA!
int getLCA(int u, int v) {
if (depth[u] < depth[v]) swap(u, v); // 保证 u 深度大于等于 v
// 第一阶段:利用倍增大步将 u 向上对齐到与 v 相同深度
for (int i = 3; i >= 0; i--) {
if (depth[u] - (1 << i) >= depth[v]) {
u = fa[u][i];
}
}
if (u == v) return u; // 如果对齐后重合,说明 v 就是 LCA
// 第二阶段:u 和 v 深度相同,同步向上翻倍跳跃
for (int i = 3; i >= 0; i--) {
if (fa[u][i] != fa[v][i]) { // 如果跳完后祖先不同,说明还没跳过 LCA
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0]; // 此时 u, v 刚好在 LCA 下方,父节点即为 LCA
}