个人学习笔记平台,欢迎大家交流

红叶~

Be humble, communicate clearly, and respect others.

LCA最近公共祖先

https://www.cnblogs.com/hulean/p/11144059.html

洛谷模板题

题目:最近公共祖先LCA

\(LCA\),最近公共祖先是指在有根树中,找出某两个结点\(u\)\(v\) 最近的公共祖先。

image-20220302142350319

怎么求?

当要求两个结点的\(LCA\),我们可以先让这两个结点跳到同一高度,然后同时往上跳,直到跳到它们公共的\(LCA\)。但是我们会浪费很多不必要的步骤,一步一步地跳太浪费时间了,有什么更简便的方法吗。这里我们需要用到一种方法——倍增的思想。所谓倍增的思想就是。

比如当前\(depth(x) = 9\),即我们要跳 \(9\) 步才能跳到根节点\(log_29 = 3\)\(9 = (1001)_2 = 8 + 1\) ,可以先跳\(8\)步,再跳\(1\)步。

这里有一个性质:由于任意步数都可以由若干个二进制数位表示(即\(2\)的倍数累加),所以我们可以先初始化当前结点的 \(2^i(i = 0,1,2...)\) 级祖先结点的值。

  • 常数优化:我们先初始化\(lg(i) = log_2(i) + 1\)的值,真实的值应该为\(lg(i) - 1\)
for(int i = 1; i <= n;i++)
    	lg[i] = lg[i-1] + (1 << log[i-1] == i);
  • 初始化结点深度和它们的 \(2^i\) 级祖先

转移: \(now\)\(2^i\) 级祖先等于 \(now\)\(2^{i-1}\)祖先的 \(2^{i-1}\)祖先 \((2 ^i = 2 ^{i-1} + 2 ^{i-1})\)

void dfs(int now, int fath) {
    fa[now][0] = fath, depth[now] = depth[fath] + 1;
    for(int i = 1;i <= lg[depth[now]] - 1; i++) {
        f[now][i] = fa[fa[now][i-1]][i-1];
    }
    for(int i = h[now]; i != -1;i = ne[i]) {
        int j = e[i];
        if(j != fath)	dfs(j, now);
    }
}
  • 倍增\(LCA\)

先让两点跳到同一高度:假设\(depth[x] - depth[y] = 10\)\(log_2[10] = 3\),先\(x = fa[x][3]\)跳到\(x\)的第\(8\)级祖先。

此时\(depth[x] - depth[y] = 2\), 再跳\(log_22 = 1,即2^1\) 级祖先到达\(y\)结点。可以看出跳的时候都是以\(2\)的倍数在跳,直到这些\(2\)的倍数加起来的和为深度差。

int LCA(int x, int y) {
    if(depth[x] < depth[y]) 	swap(x, y); // 假设x的深度更大
    while(depth[x] > depth[y]) {
        x = fa[x][lg[depth[x] - lg[depth[y]]] - 1];
    }
    if(x == y)	return x; // y是x的祖先,提前返回LCA
    for(int k = lg[depth[x]] - 1; k >= 0; )
        if(fa[x][k] != fa[y][k])	
            x = fa[x][k], y = fa[y][k];
    return fa[x][0];
}

这里讨论最后一步的情况:

image-20220302150131557

如这张图,求\(3和4\)\(LCA\)\(log_2(depth(3)) = 1\)\(fa[3][1] = 1, fa[4][1]=1\),这个时候它们已经跳到根节点来了,但是它们是相等的,我们要跳过这种情况,因为无法确定它们是\(LCA\)还是\(LCA\)的祖先。我们需要让它们跳到\(LCA\)的下一层高度。所以我们尽量选大的步数跳,从\(log_2(depth(x)) -->0\) 遍历,当跳完之后的\(x\)\(y\)不相同时说明它们还在\(LCA\)下面,这时候让它们跳上来,然后再按照之前跳的步数的一半继续跳。可见\(LCA\)算法跳的时候并不是固定的,也会有一些无效的 “跳”。那么最后跳完\(x\)\(y\)依然不相同,此时它们的上一个父节点就是\(LCA\),即\(fa[x][0]所求\)

如本题中的\(17\)\(18\)。18先跳到16时和17的高度一致。然后再一起跳

此时\(depth(17)=6\), \(log_26 = 2\),直接先跳 \(2^2\) 步到 $ 3$。16也跳到\(3\),但是由于\(fa[17][2] == fa[16][2]\),不作数,保留原地不动。

然后\(k=1\),一次跳两步(上次跳多了,减一半),17 --> 10, 16 --> 8。

最后\(k = 1\),一次跳一步,17 --> 10 --> 7, 16 --> 8 --> 5

\(k = 0\)结束过程,最终答案\(fa[7][0] = 3\)

posted @ 2022-03-02 15:14  红叶~  阅读(36)  评论(0编辑  收藏  举报