P3379 最近公共祖先模板

题目链接

顾名思义 就是求两个点的公共祖先

暴力做法就是先维护每个点的父亲 然后枚举

但显然这样的做法查询复杂度是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)


完整代码如下:



posted @ 2023-01-30 23:13  Steven24  阅读(48)  评论(1编辑  收藏  举报