LCA(最近公共祖先)-倍增算法

例题啥的还不完善,等有机会再整理吧

/*少说话,多做事*/
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stdlib.h>
//#include<bits/stdc++.h>
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
typedef long long ll;

using namespace std;

/*
 第一行:n (n头牛)(1-5e4)    Q(询问次数)1-2e5

 接下来n行:牛的高度(1-2e5)

 接下来Q行 Q次询问
 m行(m次询问)i j : 表示i与j之间的距离是多少

 DFS+ST:将树看成一个无向图,u和v的公共祖先一定在u和v之间的最短路径上,
 (1)DFS:从树T的根开始,进行深度优先遍历(将树看成一个无向图),并记录好每次到达的顶点。
 由于每条边恰好经历两次,所以一共记录了2n-1个点,用E[1~2n-1]来表示
 (2)计算R:用R[i]表示E数组中第一个值为i的元素下标
 如果R[u]<R[v],DFS访问的顺序是E[R[u],R[u]+1,....R[v]].
 虽然其中包含u的后代,但深度最小的还是u与v的公共祖先
 (3)RMQ:计算RMQ
 当R[u]>=R[v]时,LCA[T,u,v]=RMQ(L,R[v],R[u])
 否则:          LCA[T,u,v]=RMQ(L,R[u],R[v])
 */



inline int read()
{
    int f=1,x=0;
    char c=getchar();
    while(!isdigit(c)){if(c=='-')f*=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}

const int N=5e5+5;
int a[N],n,m,s,to[N<<1],nxt[N<<1],grd[N][23],d[N],cnt;

int head[N];
void add(int x,int y)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt;//邻接表
}

void dfs(int x,int fa) //根节点 0
{
    int i;
    grd[x][0]=fa;//父节点(根节点x的父亲节点为0)
    d[x]=d[fa]+1;//x节点的深度是他的父亲节点的深度+1

    for(i=1;i<21;i++) //其实到本题19就可以了,习惯到20整一点哈哈哈
    {
        grd[x][i]=grd[grd[x][i-1]][i-1];//预处理grd数组
    }

    for(i=head[x];i;i=nxt[i]) //对x节点进行遍历
    {
        if(to[i]!=fa) dfs(to[i],x); //如果终点不是他的父亲节点,那么就进行更新
    }
}

int lca(int x,int y)
{
    int i;
    if(d[x]<d[y])  //将x的深度调成更深的,然后将其跳到跟y一样深度
    {
        swap(x,y);
    }
    for(i=20;i>=0;i--) //从大到小枚举 (肯定可以跳到相同深度)
    {
        if(d[y]<=d[x]-(1<<i))//1<<i就是2^i,也可以预处理成数组
        {
            x=grd[x][i];
        }
    }
    if(x==y) return x;//当y是x的祖先时

    for(i=20;i>=0;i--)
    {
        if(grd[x][i]!=grd[y][i]) //如果不相等的话就往上跳,for循环结束后,可以确定再向上跳一次就是两者的父亲节点
        {
            x=grd[x][i];
            y=grd[y][i];//一起往上跳
        }
    }
    return grd[x][0];   //返回父节点 (就是再向上跳一次就是父亲节点)
}

/*邻接表 + 预处理grd数组 + 跳到相同深度 + 一起往上跳*/

int main()
{
    n=read();m=read();s=read(); //树的节点个数 、 询问的个数 和 树根节点的序号
    int i,j,x,y;
    for(i=1;i<n;i++)
    {
        x=read();y=read();
        add(x,y);
        add(y,x);//邻接表(也是跟链式向前星)
    }
    dfs(s,0);//预处理

    for(i=1;i<=m;i++)
    {
        x=read();y=read();
        printf("%d\n",lca(x,y));
    }

    return 0;
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务