LCA(倍增與Tarjan)

9102700發表於2024-05-28

如果我來到了我們的 LCA,我是不是就可以假裝偶遇你了


首先是倍增法,和 ST 表如出一轍呢。
注意 N 通常在 5e5 的範圍

點選檢視程式碼
int head[N], cnt;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int fa[N][20], deep[N];
void dfs(int x, int father){
    deep[x] = deep[father] + 1;
    fa[x][0] = father;
    for(int i = 1; (1 << i) <= deep[x]; i++){
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    }
    for(int i = head[x]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v != father)
            dfs(v, x);
    }
}
int LCA(int x, int y){
    if(deep[x] < deep[y]) swap(x, y);
    for(int i = 19; i >= 0; i--)
        if(deep[x] - (1 << i) >= deep[y])
            x = fa[x][i];
    if(x == y) return x;
    // x 和 y 一起向上跳
    for(int i = 19; i >= 0; i--){
        if(fa[x][i] != fa[y][i]){
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}


然後是 Tarjan 的演算法,這裡的程式碼將詢問用鏈式前向星方式儲存

點選檢視程式碼
int fa[N], ans[N];
bool vis[N];
int head[N], cnt;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}
int head_query[N], cnt_query;
struct Query{
    int from, to, num, nxt;
}q[N << 1];
void add_query(int u, int v, int num){
    q[++cnt_query].from = u;
    q[cnt_query].to = v;
    q[cnt_query].nxt = head_query[u];
    q[cnt_query].num = num;
    head_query[u] = cnt_query;
}

int find_set(int x){
    if(x != fa[x]) fa[x] = find_set(fa[x]);
    return fa[x];
}

void Tarjan(int x){
    vis[x] = true;
    for(int i = head[x]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(!vis[v]){
            Tarjan(v);
            fa[v] = x;
        }
    }
    for(int i = head_query[x]; i != 0; i = q[i].nxt){
        int v = q[i].to;
        if(vis[v]){
            ans[q[i].num] = find_set(v);
        }
    }
}
真是太簡潔了,一遍 dfs 遍歷子樹,一遍 遍歷詢問直接解出答案。

書上程式碼 find_set() 還不帶路徑壓縮,但一定要路徑壓縮