树上倍增求LCA(RMQ)

板子(反正我只是个垃圾)

//树上倍增求LCA(RMQ),其实就是个简单的二进制拆分问题
//正确性很容易证明,不证了
//思想:eg: 求x节点和y节点的Lca,比较 x 与 y 节点的深度,将树中深的节点跳到
//与浅的节点相同深度(由大到小循环),如果刚好为深度浅的节点,那么就找到了,否则一起向
//上跳,找到最小的j使得他们所跳的位置不同,但父节点是相同的,返回所跳位置的父节点就好了
#include<bits/stdc++.h>//板子
using namespace std;
const int maxn = 1000010;
int head[maxn];
int deep[maxn];//节点深度
struct{
    int v,net;
}edge[maxn];//链式前向星(ORZ,向大佬低头)
int n,m,root,cnt;
int f[maxn][21];//每个当前节点的第2 ^ j次方是哪个节点
inline void add_edge(int x,int y)
{   
    edge[cnt].v = y;
    edge[cnt].net = head[x];
    head[x] = cnt++;
}//加边
void dfs(int cur)
{
    for(int i=head[cur];i!=-1;i=edge[i].net)
    {
        if(!deep[edge[i].v])//如果节点未被访问过
        {
            deep[edge[i].v] = deep[cur] + 1;
            f[edge[i].v][0] = cur;//父节点为cur
            dfs(edge[i].v);
        }
    }
}//求节点的深度
void PRE()
{
    for(int i=1;i<=19;++i)
        for(int j=1;j<=n;++j)//n个节点预处理
            f[j][i] = f[f[j][i-1]][i-1]; //拆分二进制
}//预处理
int LCA(int x,int y)
{
    if(deep[x] < deep[y])
        swap(x,y);
    for(int i=19;i>=0;--i)
    {
        if(deep[f[x][i]] >= deep[y])//上升到同一deep
            x = f[x][i];
    }
    if(x == y)
        return x;
    //否则查找最小上升的
    for(int i=19;i>=0;--i)
    {
        if(f[x][i]!=f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
int main()
{
    ios::sync_with_stdio(false);    cin.tie(0),cout.tie(0);
    memset(head,-1,sizeof(head));//边集起点为 u 第一次出现的位置
    cin>>n>>m>>root;
    cnt = 1;
    int u,v;
    for(int i=1;i!=n;++i)//树有n-1条边,且为双向,因为得求深度,还得向上跳
    {
        cin>>u>>v;
        add_edge(u,v);
        add_edge(v,u);
    }
    deep[root] = 1;//根的深度为1
    dfs(root);
    PRE();
    for(int i=1;i<=m;++i)
    {
        cin>>u>>v;
        cout<<LCA(u,v)<<'\n';
    }
}

猜你喜欢

转载自www.cnblogs.com/newstartCY/p/11618941.html