最近公共祖先(LCA) RMQ解决

本文介绍了一种解决洛谷P3379问题的方法,该问题要求找到一棵有根多叉树中任意两点间的最近公共祖先。通过建立邻接表并使用深度优先搜索构建欧拉路径,结合预处理最小值查询,实现了高效的求解算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷p3379

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入输出格式

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入样例#1: 
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出样例#1: 
4
4
1
4
4




















#include <cstdio>

#include <iostream>
#include <algorithm>
#include <cstring>
#define M 500000*2+10
#define N 500000*2+10
using namespace std;
 
struct node
{
    int to,next;//to 出边 
//next 从这个节点出发的的上一条边的编号 
}e[M];
int tot,st[M];//tot 边的编号 
//st【i】表示从i出发的最后一条边的编号 
int n,m,root,x,y;//n条边  m次询问 root为根节点 
int f[N][20];
int dfs_list[N],depth[N],first[N];
//dfs_list【】欧拉迹
//depth【i】表示dfs_list[i]的深度
//first【now】表示now节点在欧拉迹中出现的第一个位置 
void add(int x,int y)//邻接表 
{
    e[++tot].to=y;
    e[tot].next=st[x];
    st[x]=tot;
}
 
int pow_2(int j)
{
    return (1<<j);//2^j 
}
 
void rmq_init()
{
    for (int i=1;i<=2*n-1;i++)
    f[i][0]=i;
//从dfs_list【i】到i之后2^0步的最小值 
    int i=1,j=1;
    for (int j=1;pow_2(j)<=2*n-1;j++)
    for (int i=1;i+pow_2(j)<=2*n-1;i++)
    {
        int a=depth[f[i][j-1]];
        int b=depth[f[i+pow_2((j-1))][j-1]];
        if (a<b)
        f[i][j]=f[i][j-1];
        else
            f[i][j]=f[i+pow_2((j-1))][j-1];
    }
}
int rmq_query(int l,int r)
{
    int pos;
    for (pos=0;pow_2(pos)<(r-l+1);pos++)/*!*/;/*!*/
    pos--;
    int a=depth[f[l][pos]];
    int b=depth[f[r-pow_2(pos)+1][pos]];
    if (a<b)
        return dfs_list[f[l][pos]];
    else
        return dfs_list[f[r-(1<<pos)+1][pos]];
}
int dfs_clock=0;//欧拉迹中点的个数 
void dfs(int now,int pre,int deep)//搜索欧拉迹 
{
    dfs_list[++dfs_clock]=now;
    depth[dfs_clock]=deep;
    if (!first[now])
        first[now]=dfs_clock;
    for (int i=st[now];i;i=e[i].next)//遍历 
        if (e[i].to!=pre)
//对于根节点来说没有绝对的限制 
        {
            dfs(e[i].to,now,deep+1);
            dfs_list[++dfs_clock]=now;
            depth[dfs_clock]=deep;
        }
}
main()
{
    scanf("%d%d%d",&n,&m,&root);
    for (int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(root,0,0);
    rmq_init();
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if (first[x]>first[y]) swap(x,y);
//需要从前往后搜 
        printf("%d\n",rmq_query(first[x],first[y]));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值