1. 程式人生 > 實用技巧 >LCA(最近公共祖先)

LCA(最近公共祖先)

LCA

\(LCA\)=最近公共祖先。

1.初始化\(lg\)陣列,其代表\(lg2+1\)

2.利用倍增的思想去求\(fa[u][i]\),在\(u\)點向上走\(2^i\)步時的祖先是誰。深度\(dep\)也同時求出。

3.初始化\(fa[u][0]=father\)

4.\(LCA\)

int LCA(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]>dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]-1];//一直跳直到深度相同
    }
    if(x==y)return x;
    for(int k=lg[dep[x]]-1;k>=0;--k){
        if(fa[x][k]!=fa[y][k])//向上跳從大->小,第一個不相等的點就是公共祖先的兒子
            x=fa[x][k],y=fa[y][k];
    }

    return fa[x][0];

P3379 【模板】最近公共祖先(LCA)

#include<bits/stdc++.h>
using namespace std;
//#define int long long
int n,m,s;
const int maxn=5e5+10;
int head[maxn],tot=0;

struct edge{
    int to,next;
}edge[maxn<<1];

void add(int u,int v){
    edge[++tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot;
}
int dep[maxn],fa[maxn][22],lg[maxn];


void dfs(int u,int father){
    dep[u]=dep[father]+1;
    fa[u][0]=father;
    for(int i=1;i<=lg[dep[u]];++i){
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==father)continue;
        dfs(v,u);
    }

}

int LCA(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]>dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]-1];
    }
    if(x==y)return x;
    for(int k=lg[dep[x]]-1;k>=0;--k){
        if(fa[x][k]!=fa[y][k])
            x=fa[x][k],y=fa[y][k];
    }

    return fa[x][0];
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>m>>s;
    for(int i=1;i<n;++i){
        int x,y;cin>>x>>y;
        add(x,y);add(y,x);
    }
    for(int i=1;i<maxn;++i){
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
    dfs(s,0);
    for(int i=1;i<=m;++i){
        int a,b;cin>>a>>b;
        cout<<LCA(a,b)<<endl;
    }


}

P4281[AHOI2008]緊急集合、聚會

思路

求三個點的\(lca\)\(xy,xz,yz\)。我們容易知道的是必有兩個\(lca\)是相等的(畫圖易得),然後我們選擇不相等的那個\(lca\)作為最終的終點可。(畫圖易得)

#include<bits/stdc++.h>
using namespace std;
//#define int long long
int n,m,s;
const int maxn=5e5+10;
int head[maxn],tot=0;

struct edge{
    int to,next;
}edge[maxn<<1];

void add(int u,int v){
    edge[++tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot;
}
int dep[maxn],fa[maxn][22],lg[maxn];


void dfs(int u,int father){
    dep[u]=dep[father]+1;
    fa[u][0]=father;
    for(int i=1;i<=lg[dep[u]];++i){
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==father)continue;
        dfs(v,u);
    }

}

int LCA(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]>dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]-1];
    }
    if(x==y)return x;
    for(int k=lg[dep[x]]-1;k>=0;--k){
        if(fa[x][k]!=fa[y][k])
            x=fa[x][k],y=fa[y][k];
    }

    return fa[x][0];
}

signed main(){
//    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//cin>>n>>m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;++i){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<maxn;++i){
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
    dfs(1,0);
    for(int i=1;i<=m;++i){
        int x,y,z;
//        cin>>x>>y>>z;
        scanf("%d%d%d",&x,&y,&z);
        int xy=LCA(x,y),xz=LCA(x,z),yz=LCA(y,z);
        int ans;
        if(xy==xz){
            ans=dep[x]+dep[y]+dep[z]-dep[yz]-dep[yz]-dep[xz]+dep[yz]-dep[xz];
            printf("%d %d\n",yz,ans);
//            cout<<yz<<" "<<ans<<endl;
        }else if(xy==yz){
            ans=dep[x]+dep[y]+dep[z]-dep[xz]-dep[xz]-dep[xy]+dep[xz]-dep[yz];
            printf("%d %d\n",xz,ans);
        }else{
            ans=dep[x]+dep[y]+dep[z]-dep[xy]-dep[xy]-dep[xz]+dep[xy]-dep[xz];
            printf("%d %d\n",xy,ans);
        }

    }


}