1. 程式人生 > >【總結】LCA的4種求法

【總結】LCA的4種求法

spa 另一個 int 可能 同學 需要 continue 合並 沒有

前言

LCA的求法有多重多樣,總結下來是下面這4種.希望大家可以加油!

暴力求LCA

我們考慮dfs求出每一個點的父親(在當前根下),然後直接先暴力跳到同一個深度,再同時跳

void dfs(int u,int f){
    fa[u]=f;dep[u]=dep[f]+1;
    for(re int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs(v,u);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    while(dep[u]!=dep[v])u=fa[u];
    while(u!=v)u=fa[u],v=fa[v];
    return u;
}

倍增求LCA

我們考慮每一次跳1個父親的速度太慢,那麽怎麽優化呢?

這個時候就需要用到倍增這種思想了.

沒有學過倍增的同學可以先寫一下ST表,可能會對倍增有比較深刻的理解.

我們假設這樣子一個變量\(f[i][j]\)表示點\(i\)的第\(2^j\)個父親是哪個節點.

因為每一個數都可以二進制表示,所以我們考慮每一次從大到小枚舉跳的東西,然後就可以做到\(\Theta(n\ log(n))\)

void dfs(int u,int fa){
    dep[u]=dep[fa]+1;
    for(re int i=front[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=fa)dfs(v,u),f[0][v]=u;
    }
}
int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    for(re int i=20;~i;i--)
        if(dep[a]-(1<<i)>=dep[b])
            a=f[i][a];
    if(a==b)return a;
    for(re int i=20;~i;i--)
        if(f[i][a]!=f[i][b])
            a=f[i][a],b=f[i][b];
    return f[0][a];
}

樹鏈剖分求LCA

考慮把一個樹分成輕鏈與重鏈,然後直接跳鏈就好了.

void dfs1(int u,int f){
    fa[u]=f;siz[u]=1;dep[u]=dep[f]+1;
    for(re int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u])continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}
void dfs2(int u,int tp){
    top[u]=tp;
    if(!son[u])return;
    dfs2(son[u],tp);
    for(re int i=front[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u] || v==son[u])continue;
        dfs2(v,v);
    }
}
void swap(int &a,int &b){
    int tmp=a;a=b;b=tmp;
}
int lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]>dep[v]?v:u;
}

Tarjan求LCA

考慮把每一個詢問當做一條邊處理,那麽如果這兩個都被訪問了,顯然另一個點的祖先一定是他們的LCA.

所以可以很容易地寫出這一段代碼.(註意最後合並)

int find(int x){
    if(f[x]!=x)f[x]=find(f[x]);
    return f[x];
}
void Add(int u,int v){
    to[++cnt]=v;nxt[cnt]=front[u];
    front[u]=cnt;
}
void Addques(int u,int v,int Id){
    toq[++cnt]=v;
    id[cnt]=Id;nxtq[cnt]=frontq[u];
    frontq[u]=cnt;
}
void dfs(int u,int fa){
    b[u]=1;
    for(int i=front[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=fa){
            dfs(v,u);
            for(int j=frontq[v];j;j=nxtq[j]){
                int vv=toq[j];
                if(b[vv])ans[id[j]]=find(vv);
            }
            int uu=find(u),vv=find(v);
            if(uu!=vv)f[vv]=uu;
        }
    }
}

【總結】LCA的4種求法