LCA(倍增与Tarjan与树链剖分)


首先是倍增法,和 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);
        }
    }
}

书上代码 find_set() 还不带路径压缩,但一定要路径压缩


2024.6.14 树链剖分求 LCA

int n, m, w[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 deep[N], fa[N], siz[N], son[N];
int top[N], dfn[N], nw[N], tim;

void dfs1(int u, int father){
    fa[u] = father;
    deep[u] = deep[father] + 1;
    siz[u] = 1;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == father) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(!son[u] || siz[son[u]] < siz[v]) son[u] = v; // 此行是修改之后的,同重链剖分的修改
    }
}
void dfs2(int u, int topx){
    dfn[u] = ++tim;
    nw[cnt] = w[u];
    top[u] = topx;
    if(!son[u]) return ; // 说明叶子节点了
    dfs2(son[u], topx);
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

int LCA(int x, int y){
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return deep[x] < deep[y] ? x : y;
}

树链剖分实在是太好用了,可以完全优化掉倍增了哦,就好像线段树优化掉了 ST 表吧(那就多码一点吧)

posted @ 2024-05-28 21:15  9102700  阅读(21)  评论(0)    收藏  举报