【算法-树论】树上最近公共祖先(LCA)

引入

观看例题,观察到直接向上爬的朴素 LCA 最差复杂度需要 \(2n\),会 T。

需要效率更高的方法找出公共祖先。

定义

找到两个不同的点的最近公共祖先的算法。
最近公共祖先:同时为两个点的祖先且深度尽可能大。

\(dep_x\) 为点 \(x\) 的深度。

具体实现

  • 倍增 LCA

对于每个点 \(x\) 进行祖先记录,记录方式为:记录第 \(2^0, 2^1, 2^2 \dots 2^{\log\{dep_x\}}\) 个祖先。

对于寻找点 \(x\) 和点 \(y\ (x \neq y)\) 的最近公共祖先,进行以下操作:

不妨设 \(dep_x \geq dep_y\)。

然后将点 \(x\) 的深度上调到不小于 \(dep_y\)。

如果 \(x = y\),结束。

否则将点 \(x\) 和点 \(y\) 同步上调同样的最大深度(前提是上调后不相等)。

易证此时 \(x\) 的父亲就是原 \(x\) 和 \(y\) 的最近公共祖先。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
const int man = 5e5+10, mam = 5e5+10;
class Graph {
public:
    int hed[man], len = 0;
    int nxt[mam<<1], ver[mam<<1];
    void Ins (int u, int v) {
        ver[++len] = v;
        nxt[len] = hed[u];
        hed[u] = len;
        return ;
    } 
    void DIns (int u, int v) {
        Ins(u, v), Ins(v, u);
        return ;
    }
} G;

int n, m, s;
int _log[man], dep[man];
int anc[man][50];
void dfs (int) ;
int query (int, int) ;
signed main(void) {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
  	scanf("%d%d%d", &n, &m, &s);
    for (int i = 1; i <= n; ++ i) _log[i] = _log[i-1]+(1<<_log[i-1]==i);
    for (int u, v, i = 1; i < n; ++ i) scanf("%d%d", &u, &v), G.DIns(u, v);
    dep[s] = 1;
    dfs(s);
    for (int x, y, i = 1; i <= m; ++ i) {
        scanf("%d%d", &x, &y);
        printf("%d\n", query(x, y));
    } 
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

// ---

void dfs (int x) {
    for (int i = 1; i <= _log[dep[x]]; ++ i) 
        anc[x][i] = anc[anc[x][i-1]][i-1];
    for (int i = G.hed[x]; i; i = G.nxt[i]) {
        int v = G.ver[i];
        if (dep[v]) continue;
        dep[v] = dep[x]+1;
        anc[v][0] = x;
        dfs(v);
    } return ;
} 
int query (int x, int y) {
    if (dep[x] < dep[y]) swap(x, y); // need x deeper than y
    while (dep[x] > dep[y]) {
        int k = _log[dep[x]-dep[y]];
        x = anc[x][k-1];
    } if (x == y) return x;
    for (int i = _log[dep[x]]-1; i >= 0; -- i) 
        if (anc[x][i] != anc[y][i]) x = anc[x][i], y = anc[y][i];
    return anc[x][0];
}

扩展

posted @ 2023-05-31 12:12  STA_Morlin  阅读(32)  评论(0)    收藏  举报