顾名思义 就是求两个点的公共祖先
暴力做法就是先维护每个点的父亲 然后枚举
但显然这样的作法查询复杂度是O(n)的 就TLE了(
所以需要用倍增优化:
用f[i][j]表示第i个点 向上跳 \(2^j\) 个父亲得到的点的编号
显然 有f[i][0] = fa[i] :
然后是递推式 不难看出f[i][j]可以看作是i先跳 \(2^j-1\) 格到f[i][j-1] 然后再跳 \(2^j-1\) 格 所以有:
至此 预处理完成 复杂度为O(nlogn)
那么如何查找 x 与 y 的最近公共祖先呢?
首先我们先将较低的那个跳到与另一个深度相同:
注意跳完后可能发生以下情况:
所以当 x 跳完与 y 重合 我们就直接输出:
然后 x 与 y 就能一起往上跳了
我们把 j 从大到小枚举 若 f[x][j] == f[y][j] 就说明跳过头了 :
所以 x 与 y 往上跳的条件即为 f[x][j] != f[y][j] :
当然 最后 x 与 y 会停在离最近公共祖先差一格的位置(因为再往上一格 f[x][j] 就等于 f[y][j] 没法跳)
所以此时输出 f[x][0] ( f[y][0] 一样的 写啥看心情 甚至我感觉其实写 fa[x] 都行 )
查询的复杂度为O(logn)
完整代码如下: