ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

最近公共祖先(LCA)(链式前向星+倍增法)

2021-05-17 19:03:52  阅读:174  来源: 互联网

标签:head int 44 LCA 结点 祖先 链式 公共 前向星


题目描述

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

输入格式

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

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

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

输出格式

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

输入输出样例

输入 #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

说明/提示

对于 30\%30% 的数据,N\leq 10N≤10

对于 70\%70% 的数据,N\leq 10000N≤10000

对于 100\%100% 的数据,N\leq 500000N≤500000,

样例说明:

该树结构如下:

第一次询问:2, 42,4 的最近公共祖先,故为 44。

第二次询问:3, 23,2 的最近公共祖先,故为 44。

第三次询问:3, 53,5 的最近公共祖先,故为 11。

第四次询问:1, 21,2 的最近公共祖先,故为 44。

第五次询问:4, 54,5 的最近公共祖先,故为 44。

故输出依次为 4, 4, 1, 4, 44,4,1,4,4。

#include <bits/stdc++.h>
using namespace std;
//利用倍增法,找到最近公共祖先

//链式前向星的存储方式 
/*
缺点:
1.重边不好处理
2.不能通过两点马上确定对应的边 
*/
int n,m,s;

const int maxn = 500000+5;
int k;//边号 
struct edge{
    int v;//边对应的另一个顶点 
    int next;//下一条边好,如果没有的话,为-1 
}E[maxn*2];//无向图,所以边要存两遍
int head[maxn];//head[i],记录顶点i的第一条边 

int d[maxn],p[maxn][21];
//d[i]存的是i节点对应的深度,p[i][j]对从i位置向上走2的j次方所能到达的位置


//新增的边成为顶点u所对应的第一条边 
void add(int u,int v){
    E[k].v = v;
    E[k].next = head[u];//头插法,将该边插在最前面
    head[u] = k++; 
} 


void dfs(int u,int f){
    d[u] = d[f]+1;
    p[u][0] = f;
    //边界确保最最多跳两次即达到根节点边界处 
    for(int i=1;(1<<i)<=d[u];i++){
        p[u][i] = p[p[u][i-1]][i-1];
        //相当于u的2的i次方祖先的位置 == u的2的i-1祖先的位置再往上2的i-1
        //2^i = 2^(i-1)+2^(i-1)=2*2^(i-1) 
    } 
    //从上到下递归
    for(int i=head[u];i!=-1;i=E[i].next){
        int v = E[i].v;
        if(v!=f){
            dfs(v,u);
            //保证递归的时候不陷入相应的死循环 
        } 
    } 
}

int lca(int a,int b){
    if(d[a]>d[b]){
        swap(a,b);//保证a的深度小 
    }
    //先将a,b处理成同一高度
    for(int i=20;i>=0;i--){
        //b先往上跳 
        //先从跨度大的开始跳,然后确保二者高度最终达到一致的标准 
        if(d[a]<=d[b]-(1<<i)){
            b = p[b][i];
        }
    }
    //如果移动到同一层次后,重合,则返回相应的答案 
    if(a==b) return a;
    for(int i=20;i>=0;i--){
        if(p[a][i]==p[b][i]) continue;//跳度大的地方均相同 
        //跳到小一级的地方
        a = p[a][i],b = p[b][i]; 
    } 
    return p[a][0];
    
} 
int main(){
    memset(head,-1,sizeof(head));
    int a,b; 
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(s,0);//从根节点出发,开始进行一遍预处理 
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

 

标签:head,int,44,LCA,结点,祖先,链式,公共,前向星
来源: https://www.cnblogs.com/zmachine/p/14778100.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有