1. 程式人生 > 實用技巧 >淺談多種方法求LCA(最近公共祖先)

淺談多種方法求LCA(最近公共祖先)

首先來看下例題

洛谷 P3379 https://www.luogu.com.cn/problem/P3379

相信求LCA是每位Oier學習演算法的必經之路

那麼啥是LCA呢?

引用OI wiki的定義——

那麼步入正題 如何求LCA呢?

第一種方法·樸素求法
我們選擇深度最深的點往上跳 最後兩點一定會相遇 相遇的位置即是我們要的LCA

dfs整棵樹是O(n)的,單次查詢複雜度為O(n),但實際是log(n)的(隨機樹高)

第二種方法·倍增求LCA

code如下

#include<bits/stdc++.h>
using namespace std;
const int maxn=500010
; int head[maxn],cnt=0,fa[maxn][22],Log[maxn],dep[maxn],n,m,s; struct edge { int next,to; }e[maxn<<1]; inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline void pre() { Log[1]=0; Log[2]=1; for(register int i=3;i<=maxn;++i) { Log[i]
=Log[i/2]+1; } } inline void dfs(int now,int father) { fa[now][0]=father; dep[now]=dep[father]+1; for(register int i=1;i<=Log[dep[now]];++i) { fa[now][i]=fa[fa[now][i-1]][i-1]; } for(int i=head[now];i;i=e[i].next) { if(e[i].to!=father) dfs(e[i].to,now); } } inline
int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); while(dep[x]>dep[y]) { x=fa[x][Log[dep[x]-dep[y]]-1]; } if(x==y) return x; for(register int k=Log[dep[x]];k>=0;--k) { if(fa[x][k]!=fa[y][k]) { x=fa[x][k]; y=fa[y][k]; } } return fa[x][0]; } int main() { scanf("%d%d%d",&n,&m,&s); for(register int i=1;i<=n-1;++i) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(register int i=1;i<=n;++i) { Log[i]=Log[i-1]+(1<<Log[i-1]==i); } dfs(s,0); for(register int i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }

第三種方法·Tarjan

待更新

第四種方法·樹鏈剖分

待更新

第五種方法·RMQ求LCA

講解待更新

code如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,s,cnt=0,tot=0;
const int maxn=1000010;
int head[maxn],f[maxn][25],Log[maxn],rec[maxn][25];
int fir[maxn],ver[maxn],r[maxn];//fir[i] is the first position of i
//ver[i]RMQ序;r[i]是ver[i]所處深度
struct edge
{
    int next,to;
}e[maxn<<1];
inline void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}
inline int read()
{
    char ch;
    int res,sign=1;
    while((ch=getchar())<'0'||ch>'9') 
      if(ch=='-') sign=-1;
    res=ch^48;
    while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^48);
    return res*sign;
 } 
inline void dfs(int u,int dep)//dfs處理出三個陣列
{
    fir[u]=++tot,ver[tot]=u,r[tot]=dep;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(!fir[v])
        {
            dfs(v,dep+1);
            ver[++tot]=u,r[tot]=dep;
        }
    }
}
 inline void pre()
 {
     Log[1]=0;
     Log[2]=1;
     for(register int i=3;i<=maxn;++i)
     {
         Log[i]=Log[i/2]+1;
     }
 }
 int main()
{{
    memset(head,-1,sizeof(head));
    n=read(),m=read(),s=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    dfs(s,1);
    //pre();
    for(register int i=1;i<=tot;++i)
    {
        f[i][0]=r[i],rec[i][0]=ver[i];
    }
    for(register int j=1;j<=log(tot)/log(2);++j)
       for(register int i=1;i+(1<<j)-1<=tot;++i)
       {
             if(f[i][j-1]<f[i+(1<<(j-1))][j-1])
             {
                 f[i][j]=f[i][j-1],rec[i][j]=rec[i][j-1];         
          }
          else 
          {
               f[i][j]=f[i+(1<<(j-1))][j-1],rec[i][j]=rec[i+(1<<(j-1))][j-1];
          }
       }
    for(int i=1;i<=m;i++)
    {
        int l,r;
        l=read(),r=read();
        l=fir[l],r=fir[r];
        if(l>r) swap(l,r);
        int k=0;
        while((1<<k)<=r-l+1) k++;
        k--;
        if(f[l][k]<f[r-(1<<k)+1][k]) printf("%d\n",rec[l][k]);
        else printf("%d\n",rec[r-(1<<k)+1][k]);//常見的ST表輸出
    }
    return 0;
}}

以上就是全部內容!!!後續會更新一些題目和演算法~