LCA  最近公共祖先

局限于树,倍增大法好

 

Part  1    定义LCA

LCA:对于一棵有根树,若结点z既是x的祖先,也是y的祖先,那么z就是结点x和y的公共祖先。

              PS:祖先不只是父亲,还有爷爷,曾爷爷,曾曾曾爷爷。。。。

                       我们有一棵树,定义某个点的祖先为这个点到根节点的路径上的所有点

              在 x , y 的公共祖先中,深度最大的一个结点,叫做x,y的最近公共祖先,记为 LCA(x , y)

         

               一旦找到LCA,他们的所有公共祖先都可找到,LCA一定是最深的公共祖先

用途:找任意两点的最短路,A --> LCA + B --> CA

 

 

Part 2   步骤

步骤:

  1. 如果A的深度小于B的深度,就把他们交换(只是为了方便处理,跳较深的)

                If( depA<depB) swap(A,B)

         2.  把A,B调到同一深度

         3.  A,B同时上调直到A=B,找到LCA

复杂度是O(dep)的,但是如果你的树是一条链,那么还不如不用

 

 

Part 3   优化

P[x][i],x的向上第 2^i 辈祖先,也就是从x向根节点走 2^i 步到达的结点

P[x][i] = P[P[x][i-1]][i-1]

 

(1)     优化:把A,B调到同一深度

         我们发现A和B之间有深度差

         比如 d[A] - d[B] = 19

         二级制分解19

         19=10011

          A --> P[A][4]   16

          A --> P[A][1]   2

          A --> P[A][0]   1

(2)     优化:A,B同时上调

          一开始A,B的祖先一直不一样,直到某个点,往后就都一样了

          我们不好确定最早的相同祖先的点,但是我们可以找到最后一组不相同祖先的 x , y,那么此时,他们的父亲就会是LCA(因为他们离LCA的距离总能被二进制分解啊)

           for(i= 20~0)

                If(f[A][i]!=f[B][i])

                    A,B同时上跳2^i

复杂度O(logn)

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--)
    {
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
    }
    for(int i=20;i>=0;i--) 
        if(f[x][i]!=f[y][i]) 
            x=f[x][i],y=f[y][i];
    return f[x][0];
}

 

 

 

Part  4   注意预处理

为什么数组设置 f [ ][21] 呢???

每个节点只需要用到 0~20 就好了

因为最坏的情况是一条链,节点数不会超过1e6,所以0~20就够了

void pre(int u,int fa)
{
    dep[u]=dep[fa]+1;  //子节点深度比父亲大一 
    for(int i=0;i<20;i++)  //处理u的2^k辈祖先 
       f[u][i+1]=f[f[u][i]][i];
    for(int i=head[u];i;i=edge[i].nxt )   
    {
        int v=edge[i].to ;
        if(v==fa) continue;  
        f[v][0]=u; //v的父节点是u 
        pre(v,u);  //递归处理儿子
    }
}

 

 

 

Part  5  code

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstdlib>

using namespace std;

inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

const int maxn=5e5+10;
int n,m,s,edge_num=0;
int head[maxn*2],dep[maxn],f[maxn][21];
struct node
{
    int nxt,to;
}edge[maxn*2];

void addedge(int u,int v)
{
    edge[++edge_num].to =v;edge[edge_num].nxt =head[u];head[u]=edge_num;
    edge[++edge_num].to =u;edge[edge_num].nxt =head[v];head[v]=edge_num;
}

void pre(int u,int fa)
{
    dep[u]=dep[fa]+1;
    for(int i=0;i<20;i++) 
       f[u][i+1]=f[f[u][i]][i];
    for(int i=head[u];i;i=edge[i].nxt )
    {
        int v=edge[i].to ;
        if(v==fa) continue;
        f[v][0]=u;
        pre(v,u);
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--)
    {
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
    }
    for(int i=20;i>=0;i--) 
        if(f[x][i]!=f[y][i]) 
            x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main()
{
    n=read();m=read();s=read();
    int x,y;
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        addedge(x,y);
    }
    pre(s,0);
    for(int i=1;i<=m;i++)
    {
        x=read();y=read();
        printf("%d\n",lca(x,y));
    }
    return 0;
}


 
LCA 模板

相关文章:

  • 2022-02-03
  • 2021-07-19
  • 2022-12-23
猜你喜欢
  • 2021-11-30
  • 2021-05-23
  • 2022-01-21
  • 2021-11-03
  • 2021-12-07
相关资源
相似解决方案