Jerry's Blog

Back

题目:P3379 最近公共祖先(LCA)

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先.

输入格式#

第一行包含三个正整数 N,M,SN,M,S,分别表示树的结点个数、询问的个数和树根结点的序号.

接下来 N1N-1 行每行包含两个正整数 x,yx, y,表示 xx 结点和 yy 结点之间有一条直接连接的边(数据保证可以构成树).

接下来 MM 行每行包含两个正整数 a,ba, b,表示询问 aa 结点和 bb 结点的最近公共祖先.

输出格式#

输出包含 MM 行,每行包含一个正整数,依次为每一个询问的结果.

数据范围#

对于 100%100\% 的数据,1N,M5×1051 \leq N,M\leq 5\times10^51x,y,a,bN1 \leq x, y,a ,b \leq N不保证 aba \neq b

分析#

LCA(Lowest Common Ancestor)即树上两个节点 u,vu,v 路径上离根最近的公共节点.

常用方法有:

  • 倍增法(预处理 O(nlogn)O(n\log n),单次查询 O(logn)O(\log n)
  • 树链剖分(重链剖分)
  • Tarjan 离线算法

最常用、易实现的是倍增法

倍增法需要:

  • 预处理每个节点的 2k2^k 级祖先(即 fa[u][k]fa[u][k] 表示 uu 的第 2k2^k 级祖先).
  • 查询时先将 u,vu,v 跳到同一深度,然后一起向上跳,距离从 2logn2^{\log n}202^0,如果跳到相同的节点就调回来,下一次少跳一些,直到找到最近公共祖先.

具体实现步骤:

  1. 建树与初始化
  • 用邻接表存树.
  • 记录每个节点的深度 deep[u]deep[u]
  • 记录每个节点的父节点 fa[u][0]fa[u][0]
  1. 预处理倍增表
  • 对每个节点 uufa[u][k]=fa[fa[u][k1]][k1]fa[u][k] = fa[fa[u][k-1]][k-1]
  • 预处理 kk11logn\log n
  1. 查询 LCA
  • u,vu,v 深度不同,先将较深的节点向上跳到同一深度.
  • 然后从大到小枚举 kk,若 fa[u][k]fa[v][k]fa[u][k] \neq fa[v][k],则 u,vu,v 同时跳到各自的 2k2^k 级祖先.
  • 最后 uuvv 的父节点就是 LCA.

复杂度分析#

  • 预处理:O(nlogn)O(n\log n)
  • 单次查询:O(logn)O(\log n)

标程#

最近公共祖先
https://blog.jerrylab.top/blog/graph/lca
Author Jerry
Published at 2026年3月2日