LCA---倍增法(也许后续会出tarjan法)

 终于看懂LCA倍增法,所以想要分享一下(本人是学y总的做法)

LCA---即找两个结点最近公共祖先

那么先来看一道题吧

这是洛谷一道LCA模板题

首先我们需要fa[N][20],depth[N],p[N]数组,fa数组是用来倍增的,也就是存储该结点的祖先(包括它的父亲和爷爷),depth数组是存储当前结点的深度,我们把根节点的depth定为1,p数组用来模拟队列

那么现在我们先把前文以及main函数部分给出,方便后续理解

#include<iostream>
#include<vector>
#include<cstring>

using namespace std;

const int N = 5e5 + 10;

int n, m, s;
vector<int> edges[N];
int fa[N][21];
int depth[N];
int q[N]; // 固定大小队列





int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    memset(fa, 0, sizeof fa); 

    cin >> n >> m >> s;
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        edges[x].push_back(y);
        edges[y].push_back(x);
    }
    bfs();
    while (m--) {
        int a, b;
        cin >> a >> b;
        cout << lca(a, b) << '\n';
    }
    return 0;
}

接下来我们顺着main函数步骤讲解,首先是bfs部分

void bfs() {
    memset(depth, 0x3f, sizeof depth);
    depth[0] = 0;   // 哨兵节点
    depth[s] = 1;    // 根节点深度为1
    int hh = 0, tt = 0;
    q[tt++] = s;     // 初始队列为根节点
    while (hh < tt) { // 正确终止条件
        int u = q[hh++];
        for (int v : edges[u]) {
            if (depth[v] > depth[u] + 1) {
                depth[v] = depth[u] + 1;
                fa[v][0] = u;
                // 递推祖先数组
                for (int k = 1; k <= 20; k++) {
                    fa[v][k] = fa[fa[v][k - 1]][k - 1];
                }
                q[tt++] = v; // 仅在更新后入队
            }
        }
    }
}

这个递推祖先数组就是把当前结点的祖先都存放在fa数组,利用了二进制的思想

主要是lca部分

int lca(int a, int b) {
    if (depth[a] < depth[b]) swap(a, b);
    // 对齐深度
    for (int k = 20; k >= 0; k--) {
        if (depth[fa[a][k]] >= depth[b]) {
            a = fa[a][k];
        }
    }
    if (a == b) return a;
    // 同步跳跃
    for (int k = 20; k >= 0; k--) {
        if (fa[a][k] != fa[b][k]) {
            a = fa[a][k];
            b = fa[b][k];
        }
    }
    return fa[a][0];
}

我们来个图讲解一下吧

首先是对齐深度

红色代表结点深度,那么对齐深度功能就是通过fa数组将9不断找祖先,知道祖先位置与7的深度对齐,那么此时9就会找到3

然后同步跳跃部分,你可能会疑惑为什么我上面这个深度对齐了,那直接返回他们两的上一个结点不就行了,其实上面的图只是个特例

比如这张图我要找9和10的祖先,那么先对齐深度,9结点变成5结点,那么发现这两个的父节点不相同,那么此时同步跳跃就发力了,他会将10结点和5结点同步跳跃,也就是每次执行他两深度不变,直到找到最近祖先2,那么就停止

好了,感谢观看,虽然本人写这篇博客是码性大发,哈哈哈,就当加深理解了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值