倍增LCA与Tarjan
倍增 LCA
倍增 LCA
1、LCA:输入树的节点个数 $n$,这棵树以节点 $1$ 为根,接下来 $n-1$ 行,分别输入节点 $2 \to n$ 的父节点,再输入询问次数 $q$,每个询问给出两个整数 $u, v$,输出 $u, v$ 的最近公共祖先 $lca(u,v)$
2、最近公共祖先 LCA:深度最大的公共祖先,即几个节点的父节点链从下向上最早相交的点 (可以是这两个点中的一个)。性质:$u \to v$ 的简单路径可以分为两条链,即 $u \to lca(u,v)$ 和 $lca(u,v) \to v$;类似于 $max, min, gcd$ 等运算,其满足 $lca(x,y,z) = lca(x,lca(y,z))$;多个点的 $lca(x_1,x_2,…,x_k)$ 等价于 DFS 序最小点 和 DFS 序最大点的 $lca$
3、倍增 LCA:类似于ST 表,我们令 $fa[x][j]$ 表示节点 $x$ 向上走 $2^j$ 所到达的点,例如对于节点 $x$,其向上第一个父节点为 $fa[x][0]$,第二个为 $fa[x][1]$,第四个为 $fa[x][2]$,第八个为 $fa[x][3]$;此外,从根开始以节点深度从上到下标记层次,用 $dep[x]$ 记录 $x$ 的深度。这样的结构可以实现快速跳跃,要从 $x$ 向上跳跃到目标层次,可以贪心地先迈大步再迈小步:从大到小枚举 $j$,再检查 $fa[x][j]$ 是否超过目标层次,超过则不操作,不超过则 $x$ 跳跃到当前 $fa[x][j]$,继续向下枚举。例如,如果要从第 $8$ 层跳跃到第 $2$ 层,二者相隔 $6$ 个节点:为方便,从 $j = 3$ 开始枚举,此时 $fa[x][j]$ 是向上 $8$ 个节点的位置,超出目标范围不操作;继续枚举 $j = 2$,此时 $fa[x][j]$ 为向上 $4$ 个节点的位置,令 $x$ 跳到该位置,此时还差 $2$ 个节点到目标;继续枚举 $j = 1$,$x$ 跳到当前 $fa[x][j]$ 到达目标节点。可以发现,起始相隔的 $6$ 个节点二进制为 $110_2$,$1$ 的位置恰好指示了对应位置 $j$ 需要跳跃
4、实现上,初始化最上面虚拟的点 $fa[1][0] = 0$ (无意义),操作上需要求 $fa[][]$ 数组,再求 $lca$。对于求数组,可以通过 DFS 得到,转移方程为 $fa[x][j] = fa[fa[x][j-1]][j-1]$,例如求 $fa[x][2] = fa[fa[x][1]][1]$ (求 $x$ 向上走 $2^2$ 步,等同于从 $x$ 向上走 $2^1$ 步再走 $2^1$ 步),在 DFS 过程中,上面的点的 $fa[][]$ (即转移方程右侧)是已知的;此外,对于 $dep[]$ 有 $dep[x] = dep[fa[x][0]] + 1$ (深度为父节点 $+1$)。对于求 $lca$,给定两点 $x, y$,首先将深度大的(假设是 $x$)向上跳至二者在同一深度(用上面的方法跳跃),判断 $x, y$ 是否相等(因为可能 $y$ 就是 $x$ 的祖先),相等就输出 $y$;如果不相等,那么二者一起向上跳,依然从大到小枚举 $j$,但条件为若 $fa[x][j] = fa[y][j]$ 则不跳 (始终保持两个点不相等),这样操作后,最终两个点会留在 $lca$ 的子节点,所以返回父节点 $fa[x][0]$ 即为 $lca$