LCA问题
LCA
LCA 的问题是很经典的,我这一次就来讲解一下 LCA 的求法,就先从一道模板题入手吧
题目
输入格式
第一行包含三个正整数 \(N,M,S\) 分别表示树的结点个数、询问的个数和树根结点的序号。
接下来 \(N-1\) 行每行包含两个正整数 \(x, y\) 表示 \(x\) 结点和 \(y\) 结点之间有一条直接连接的边(数据保证可以构成树)。
接下来 \(M\) 行每行包含两个正整数 \(a, b\) 表示询问 \(a\) 结点和 \(b\) 结点的最近公共祖先。
输出格式
输出包含 \(M\) 行,每行包含一个正整数,依次为每一个询问的结果。
说明/提示
对于 \(30\%\) 的数据,\(N\leq 10,N \leq 10\)。
对于 \(70\%\) 的数据,\(N\leq 10000,N \leq 10000\)。
对于 \(100\%\) 的数据,\(N\leq 500000 , N \leq 500000\)
方法
LCA 的一些性质
- 两点的距离 : $ d(u,v) = h(u) + h(v) + h((u,v) )$ ,\(h\) 代表某一点的深度
- 两点的最近公共祖先必定处在树上两点间的最短路上;
朴素算法
首先,我们想到最暴力的方法,先把整个树 DFS 一下,顺便将每个点的深度记录下来,将要查找的两个点中每次找深度最大的点,然后向上跳一格,最后两个点一定会相遇,相遇的点就一定是他们的 LCA 。
下面是 DFS 的代码
1 |
|
然后是 LCA 的核心代码 :
1 |
|
很简单,就不详细讲了, 预处理时间复杂度是 \(\mathcal{O}(n)\) , 单次查询时间复杂度为 \(\Theta(n \log n)\) (随机树上) ,时间有点慢,遇到大数据就不行了,我们需要优化
倍增 LCA
这里先来了解一下倍增(更容易理解),顾名思义,一倍一倍的增加,就比如一步一步跳 7 格,但是我们可以先跳 \(2^2= 4\) 次,再跳 \(2\) 次,最后跳 \(1\) 次,原本需要跳 7 次,通过倍增只需要跳 3 次。
这里可以倍增首先基于一个推论 :
任意整数可以表示成若干个 2 的次幂项的和
\(eg. 7 = 2^2 + 2^1 + 2^0\)
\(10 = 2^3 + 2^1\)
下面来讲一下倍增 LCA :
显然可以将一步一步跳变成倍增,在 DFS 时可以通过 \(2^i = 2^{i-1}+2^{i-1}\) 预处理 \(fa_i\) 的位置。
在查询时可以从小到大倍增(\(2^0,2^1,2^2\dots\))来将 \(u,v\) 跳到同一深度,在同时 跳的时候从大到小倍增(\(2^i,2^{i-1}\dots 2^1,2^0\)),如果两个父亲不一样,就跳,那么最后的 LCA 是 \(fa_0\)
倍增 LCA 预处理时间复杂度为 \(\mathcal{O}(n \log n)\) ,单次查询时间为 \(\mathcal{O}(\log n)\) 。
这个时间复杂度大部分题目都是可以接受的。
代码:
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!