LCA(least common ancestors)最近公共祖先

指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先。

定义到此。

那么怎么求LCA?

对于朴素思想,就是我要一步一步往上爬。。一步一步走。先把结点x和y整到同一深度,然后再一次一个深度的往上查,直到祖先一样才break(明显是个while)

但是,一步一步实在是太慢了,所以不能脚踏实地地走

那么,考虑跳着走,

跳着走的条件就是要满足一步步数尽可能多并且不跳过了。当然你跳过了在一步一步往下找也行啊QWQ

于是,在满足这两个条件的情况下,我们有了LCA算法:

一步条2的n次方。

对于每一步跳跃,我们要预处理一个二维数组f(father)f[x][i],表示对于当前的x结点,往上跳2^i个祖先,特别的,像数学中的,f[x][0]就是x的一代祖先。于是我们要用一个dfs预处理来解决这个f数组。后面我们会提到;

处理完f数组,那就很简单了。

输入x和y,表示要求结点x和结点y的LCA,那么我们开始求LCA:

对于x和y在不同高度,我们先把他们拉到一个高度,同样不能一步一步走,也要用到f数组。

这里我们要提前了解到一个定理:对于任意一个非零整数,我们都可以将他用2的次幂表示出来。也就是such as  : 11=2^3+2^1+2^0。这倒也不用证明,就像每个数都可以用二进制表示一样。

接着讲:在把x和y弄到同一高度时,我们要先做的是设x的深度dep要比y的深度dep要大,如果dep[y]>dep[x],那么把他们交换。原因是如果不交换,那么我们需要判断两种情况,用两个if以及几乎相同的代码。。(乱七八糟还占内存)

然后用一个判断    if(dep[f[x][i]]>=dep[y])x=f[x][i];(此时x深度大于y),在这里用外层for循环来枚举i,但是i一定要从大到小!。为什么?

安利一个故事(其实也不算):一个玻璃瓶,装了几块石头,满到瓶口。满了吗?没有。又装了一些沙子,满到瓶口。满了吗?没有。最后又装满了水,满到瓶口。终于满了。

那么,类比于x和y的深度的距离,这个瓶子的容积也是同样道理。从2的尽可能大的次幂去找,一旦能找到能接近他们的i,就更新dep[x],直到相等。类似于无限逼近,最后值相等的过程。如果i相等了,就说明他们的深度已经在同一层了。

与倍增到同一深度的过程相比,倍增找公共祖先也是类似的,但是有一点不同,就是你不知道什么时候找到他们的公共祖先,因此就没有查找的上限,那么就让他们尽可能接近。因此条件改成了这个:

if(f[x][i]!=f[y][i])
        {
            x=f[x][i];y=f[y][i];
        }

在执行完这个语句后,我们成功让x和y变成了某一节点z的左右儿子!

因为左右儿子是最接近但是又不相等的。

那么我们随便取其中一个找爸爸,就找到了LCA。

啊。。。。。。喘口气

AC代码:

有关链式存图不懂的的点这里。

#include<cstdio>
#include<iostream>
using namespace std;
int n,m,s,num=,head[],dep[],f[][];
int a1,a2;
struct edg{
int next,to;
}edge[];
void edge_add(int u,int v)//链式前向星存图
{
num++;
edge[num].next=head[u];edge[num].to=v;head[u]=num;
edge[++num].next=head[v];edge[num].to=u;head[v]=num;
}
void dfs(int u,int father)//对应深搜预处理f数组
{
dep[u]=dep[father]+;
for(int i=;(<<i)<=dep[u];i++)
{
f[u][i]=f[f[u][i-]][i-];
}
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==father)continue;//双向图需要判断是不是父亲节点
f[v][]=u;
dfs(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=;i>=;i--)//从大到小枚举使x和y到了同一层
{
if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
}
for(int i=;i>=;i--)//从大到小枚举
{
if(f[x][i]!=f[y][i])//尽可能接近
{
x=f[x][i];y=f[y][i];
}
}
return f[x][];//随便找一个**输出
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<n;i++)
{
scanf("%d",&a1);scanf("%d",&a2);
edge_add(a1,a2);//链式存边
}
dfs(s,);
for(int i=;i<=m;i++)
{
scanf("%d %d",&a1,&a2);
printf("%d\n",lca(a1,a2));//求两个节点的LCA
}
}

完结。。

树上倍增求LCA详解的更多相关文章

  1. [学习笔记] 树上倍增求LCA

    倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...

  2. 树上倍增求LCA(最近公共祖先)

    前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...

  3. [算法]树上倍增求LCA

    LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...

  4. dfs序+RMQ求LCA详解

    首先安利自己倍增求LCA的博客,前置(算不上)知识在此. LCA有3种求法:倍增求lca(上面qwq),树链剖分求lca(什么时候会了树链剖分再说.),还有,标题. 是的你也来和我一起学习这个了qwq ...

  5. 树上倍增求LCA及例题

    先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...

  6. Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)

    题面 传送门 题目大意: 给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: rel ...

  7. [luogu3379]最近公共祖先(树上倍增求LCA)

    题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...

  8. CF 519E(树上倍增求lca)

    传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时, ...

  9. 树上倍增求LCA

    大概思想就是,节点$i$的第$2^{j}$个父节点是他第$2^{j-1}$个父亲的第$2^{j-1}$个父亲 然后可以$O(nlogn)$时间内解决…… 没了? //fa[i][j]表示i的第2^j个 ...

随机推荐

  1. CentOS7 执行yum 命令出错

    http://blog.csdn.net/weicuidi/article/details/52935100 https://www.cnblogs.com/pqchao/p/6428947.html

  2. Python:Base2(List和Tuple类型, 条件判断和循环,Dict和Set类型)

    1.Python创建list: Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: &g ...

  3. java:redis(redis安装配置,redis的伪集群配置)

    1.redis安装配置: .安装gcc : yum install gcc-c++ .使用FTP工具FileZilla上传redis安装包到linux根目录下(当前步骤可以替换为:在root目录下执行 ...

  4. C++学习笔记-多态的实现原理

    深入了解多态的实现原理,有助于提高对于多态的认识 多态基础 多态的实现效果 多态:同样的调用语句有多种不同的表现形态 多态实现的三个条件 有继承.有virtual重写.有父类指针(引用)指向子类对象 ...

  5. 【GO】一个容易踩坑的内外变量屏蔽问题

    package main import ( "errors" "fmt" ) func et()(string,error){ return "&qu ...

  6. c++ 读入优化通用模板

    struct ioss { #define endl '\n' ; char obuf[LEN], *oh = obuf; std::streambuf *fb; ioss() { ios::sync ...

  7. Java小知识---Java请求一个URL。获取网站返回的数据

    对url发送请求,获得返回值: public static String SendGET(String url,String param){ String result="";// ...

  8. Git 发生Another git process seems to be running in this repository, e.g. an editor opened by 'git commit'.错误

    Git 发生 Unable to create 'D:/Model/test/.git/index.lock': File exists. Another git process seems to b ...

  9. Django中ORM操作提升性能

    提升orm操作性能注意的点 优化一:尽量不查对象,能用values就是用values 直接使用对象查询的结果是5条sql语句 def youhua(request): # 使用对象查 obj_list ...

  10. JS中的继承(下)

    JS中的继承(下) 在上一篇 JS中的继承(上) 我们介绍了3种比较常用的js继承方法,如果你没看过,那么建议你先看一下,因为接下来要写的内容, 是建立在此基础上的.另外本文作为我个人的读书笔记,才疏 ...