最近公共祖先(LCA)问题的树剖实现 (模板)

我来存个档,防止忘记!2333

传送门:https://daniu.luogu.org/problem/show?pid=3379

题目描述

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

输入输出格式

输入格式:

第一行包含三个正整数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

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

样例说明:

该树结构如下:

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

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

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

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

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

故输出依次为4、4、1、4、4。

一些定义:

  • 重孩子:儿子节点所有孩子中size最大的;
  • 轻孩子:儿子节点中除了重儿子的;
  • 重边:连接重儿子的边;
  • 轻边:连接轻儿子的边;
  • 重链:重边连成的链;
  • 轻链:轻边连成的链;

思想:树剖LCA是让当前两个节点所在链首深度大的走到链首的父节点直到在一条重链上,返回深度小的节点即为LCA。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

int n,m,s,cnt,x,y;
struct sdt
{
    int to,nxt;
}a[1000005];
int head[500005],top[500005],deep[500005],par[500005],son[500005],num[500005];

int read()
{
    int x=0;char c=getchar();
    while(c<48||c>57)c=getchar();
    while(c>47&&c<58)x*=10,x+=c-48,c=getchar();
    return x;
}

void add(int fr,int tox)
{
    a[++cnt].to=tox;
    a[cnt].nxt=head[fr];
    head[fr]=cnt;
}

void build(int x)
{
    deep[x]=deep[par[x]]+1;
    num[x]=1;
    for(int i=head[x];i;i=a[i].nxt)
    {
        int y=a[i].to;
        if(par[x]!=y && !par[y])
        {
            par[y]=x;
            build(y);
            num[x]+=num[y];
            if(num[y]>num[son[x]])son[x]=y;
        }
    }
}

void find(int x)
{
    if(son[x])top[son[x]]=top[x]; else return ;
    for(int i=head[x];i;i=a[i].nxt)
    {
        int y=a[i].to;
        if(par[y]==x)
        {
            if(y!=son[x])top[y]=y;
            find(y);
        }
    }
}

int query(int x,int y)
{
    if(top[x]==top[y])
    {
        if(deep[x]<deep[y])return x; else return y;
    }
    if(deep[top[x]]<deep[top[y]])y=par[top[y]]; else x=par[top[x]];
    return query(x,y);
}

int main()
{
    n=read();m=read();s=read();
    for(int i=1;i<=n-1;i++)
    {
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    deep[0]=-1;
    build(s);
    top[s]=s;
    find(s);

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

  

时间: 2024-11-05 11:44:18

最近公共祖先(LCA)问题的树剖实现 (模板)的相关文章

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

求最近公共祖先(LCA)板子 x

LCA目前比较流行的算法主要有tarjian,倍增和树链剖分 1)tarjian 是一种离线算法,需要提前知道所有询问对 算法如下 1.读入所有询问对(u,v),并建好树(建议邻接表) 2.初始化每个节点各属一个并查集,都指向自己 3.对整棵树进行dfs(深度优先搜索)遍历 每处理到一个新节点(u)时看他的另一半(询问对象v)是否visit过,如果visit过了,则这组询问对的lca即v的并查集的根节点,若没有visit过,则继续向下深搜,该节点记为已visit 每当回溯的时候都将子节点的并查集

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

二叉查找树(6) - 最近公共祖先LCA

给定二叉查找树中的两个节点,求它们的最近公共祖先(Lowest Common Ancestor - LCA). 在详细介绍之前,可以先参考下这篇文章"二叉树(70) - 最近公共祖先[1]" 函数原型定义如下: Node *getLCA(Node* root, int n1, int n2) 其中n1和n2是指定的两个节点值. 例如, 上述BST中,10和14的LCA是12,而8和14的LCA是8. 下面是来自Wikipedia的关于LCA的定义: 假设存在一颗二叉树T, 其中节点n1

POJ 1330 最近公共祖先LCA(Tarjan离线做法)

题目链接:http://poj.org/problem?id=1330 题目大意十分明了简单,就是给定一棵树,求某两个子节点的最近公共祖先,如果尚不清楚LCA的同学,可以左转百度等进行学习. 稍微需要注意的是,建树顺序需要按照题目给定的顺序进行,也就是说根被设定成第一个给出的结点,如样例2. 此题网上题解颇多,但是多是使用的邻接表存图,于是我这里采用了边表,不过实质上Tarjan的部分思想都是一样的,均利用了并查集. AC代码: #include <cstdio> #include <c

最小公共祖先lca

3.神秘国度的爱情故事 题目要求:某个太空神秘国度中有很多美丽的小村,从太空中可以想见,小村间有路相连,更精确一点说,任意两村之间有且仅有一条路径.小村 A 中有位年轻人爱上了自己村里的美丽姑娘.每天早晨,姑娘都会去小村 B 里的面包房工作,傍晚 6 点回到家.年轻人终于决定要向姑娘表白,他打算在小村 C 等着姑娘路过的时候把爱慕说出来.问题是,他不能确定小村 C 是否在小村 B 到小村 A 之间的路径上.你可以帮他解决这个问题吗? 输入要求:输入由若干组测试数据组成.每组数据的第 1 行包含一

最近公共祖先LCA详细解法

最近公共祖先(LCA)是树上倍增的一种典型例题. 问题描述:给定一棵具有n个节点的树,询问两个节点x,y的最近的公共祖先. 分析:首先我们想到的是暴力算法,当然这个算法妥妥的会TLE掉,所以我们采用倍增优化的方法来进行实现,也就是我们今天介绍的重点. 所谓倍增,就是按2的倍数来增大,按1,2,4,8,16......来跳.不过我们不能按正着的顺序跳,需要按......16,8,4,2,1的顺序来跳.因为如果从小的开始跳,有可能出现"悔棋"的现象(5 不等于 1 + 2 +4,这样就必须