写在前面

目录

一、LCA的定义

二、暴力法求LCA

三、倍增法求LCA

四、树链剖分求LCA

五、LCA典型例题

题目完成度


一、LCA的定义

LCA指的是最近公共祖先。具体地,给定一棵有根树,若结点z既是结点x的祖先,又是结点y的祖先,则称z是x,y的公共祖先。在x,y的公共祖先中,深度最大的一个结点称为x,y的最近公共祖先,记为LCA(x,y)

go back


、暴力法求LCA

暴力法,顾名思义,非常暴力,这里简单介绍一下

先DFS一遍找出每个点的深度,然后先从深度大的往上跳,跳到x,y两个点深度相同。

如果发现此时x和y是同一个结点,那么原本深度小的那个结点就是两个点的最近公共祖先。

如果不是同一个点,那么就两个点一起同时往上跳,直到发现两个点相同,则此时到达的这个结点为x,y两点的最近公共祖先。

484很简单?QWQ

那我等下放个代码,就酱紫吧

int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);//默认x深度大于y
while(dep[x]>dep[y]) x=fa[x];//x每次变成自己的父结点,即往上跳一步
if(x==y) return y;
//如果此时两个结点相同,那么原本深度较小的结点就是LCA
while(x!=y)
x=fa[x],y=fa[y];//两个点同时一步步往上跳
return x;//此时两个节点相同,都是LCA
}

其实还有一种暴力的方法

就是先让其中一个点一路跳到根结点,标记经过的结点,然后另一个点也往上跳,遇到的第一个标记了的结点就是LCA

同样放下代码

int LCA(int x,int y){
while(x!=root){//root为根结点
x=fa[x];
visit[x]=;//标记
}
while(!visit[y])//如果遇到被标记了的就找到了LCA
y=fa[y];
return y;
}

go back


三、倍增法求LCA

这是一个非常重要的算法啦!一定要记住QAQ

我来讲一讲啦

【预处理】

设f[x][k]表示x结点的2k辈祖先,即从x向根结点走2k步到达的结点

如果该结点不存在,则令f[x][k]=0,f[x][0]就是x的父亲结点

因为2k=2k-1+2k-1,所以对于1≤k≤logn,有f[x][k]=f[f[x][k-1]][k-1]

预处理部分的时间复杂度为O(nlongn)

void ready(int x,int father){
dep[x]=dep[father]+;//计算深度
go(i,,)//预处理f数组
f[x][i+]=f[f[x][i]][i];
for(int i=head[x];i;i=next[i]){
int y=to[i];//用链式前向星存边
if(y==father) continue;
f[y][]=x;
ready(y,x);
}
}

【查找LCA】

预处理过后可以对多个x,y查找LCA,每次的时间复杂度均为O(nlogn)

具体操作如下:

1.假设dep[x]≥dep[y],如果不成立可以交换

2.用二进制拆分思想把x上调到与y同一深度

其实就是依次尝试让x向上跳k=2logn...21,20,若到达的结点比y深,则令x=f[x][k]

3.和上面说的一样,若此时x与y相等,则y就是LCA

4.若此时x≠y,那么x和y同时往上跳,同样用二进制拆分思想,把x和y同时向上跳,并保持深度一致且不相会

与上面调整x时一样,让x和y尝试向上走k=2logn...21,20步,若f[x][k]≠f[y][k](即两点不相会),则令x=f[x][k],y=f[y][k]

5.结束时x和y必然只差一步就相会了,所以他们的父节点f[x][0]就是LCA啦!QWQ

int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
back(i,,){
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
//只要x的深度没有比y小就可以继续跳
if(x==y) return x;
}
back(i,,){
if(f[x][i]!=f[y][i])//跳的过程中要保证两点不相会
x=f[x][i],y=f[y][i];
}
return f[x][];
}

最后再放一个完整版代码吧QWQ

 #include<bits/stdc++.h>
#define go(i,a,b) for(register int i=a;i<=b;i++)
#define back(i,a,b) for(register int i=a;i>=b;i--)
using namespace std;
const int N=;
int n,m;
int dep[N],head[N];
int f[N][];
int next[N*],to[N*],num=;
int last=;
int fr(){
int w=,q=;
char ch=getchar();
while(ch<''||ch>''){
if(ch=='-') q=-;
ch=getchar();
}
while(ch<=''&&ch>='')
w=(w<<)+(w<<)+ch-'',ch=getchar();
return w*q;
}
void ready(int x,int father){
dep[x]=dep[father]+;
go(i,,)
f[x][i+]=f[f[x][i]][i];
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==father) continue;
f[y][]=x;
ready(y,x);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
back(i,,){
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
}
back(i,,){
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
}
return f[x][];
}
int main(){
n=fr();
int root,father;
go(i,,n){
father=fr();
if(father==) root=i;
next[++num]=head[father];
to[num]=i;
head[father]=num;
next[++num]=head[i];
to[num]=father;
head[i]=num;
}
ready(root,);
m=fr();
while(m--){
int x=fr(),y=fr();
last=LCA(x,y);
printf("%d\n",last);
}
return ;
}

代码戳这里

go back


四、树链剖分求LCA

咕咕咕咕咕

go back


五、LCA典型例题

咕咕咕咕

go back

LCA学习笔记的更多相关文章

  1. 倍增LCA学习笔记

    前言 ​ "倍增",作为一种二进制拆分思想,广泛用于各中算法,如\(ST\)表,求解\(LCA\)等等...今天,我们仅讨论用该思想来求解树上两个节点的\(LCA\)(最近公共祖先 ...

  2. 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))

    倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...

  3. 树链剖分 树剖求lca 学习笔记

    树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...

  4. kruskal重构树学习笔记

    \(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal​\) 求最小(大)生成树,树上求 \(lca​\). 算法详 ...

  5. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  6. Day 4 学习笔记 各种图论

    Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...

  7. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  8. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  9. OI知识点|NOIP考点|省选考点|教程与学习笔记合集

    点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...

随机推荐

  1. Java之匿名内部类详解

    前言 本文讲解Java中最后一种内部类,叫做匿名内部类.顾名思义,所谓的匿名内部类就是一个没有显式的名字的内部类,在实际开发中,此种内部类用的是非常多的. 匿名内部类 本质:匿名内部类会隐式的继承一个 ...

  2. Exp6 信息搜集与漏洞扫描 20165110

    Exp6 信息搜集与漏洞扫描 20165110 一.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 二.实践内容 (1)各种搜索技巧的应用 (2)DNS IP注册信息的查询 (3)基本的扫描 ...

  3. left join inner join 区别

    left 以左表为准,左表在右表没有对应的记录,也为显示(右表字段填空). inner 需要满足两张表都有记录. 不管哪种join 一对多最终的结局 只会是多条记录

  4. 【原创】JAVA面试解析(有赞一面)

    本文的题目出自博客 http://www.54tianzhisheng.cn/2018/07/12/youzan/ 但是作者没有给出答案,博主斗胆来制作答案版. 引言 说在前面的话: 本文适合人群:急 ...

  5. js 运行机制

    <script> console.log(1) setTimeout(function(){ console.log(3) },0) console.log(2) </script& ...

  6. Surjectivity is stable under base change

    Nowadays, I close a new small case. Proposition. For a surjective morphism between scheme $X\stackre ...

  7. python的web运用

    ---恢复内容开始--- 对于大多数学过编程语言的人来说都知道大部分的编程语言都可以用来开发web运用,对于python来说也是可以的,不过对于web开发来说用python你可以选择 两个不同的框架, ...

  8. 基于Flask 实现Web微信登陆

    网页版微信登陆网址 https://login.wx.qq.com/ 获取微信登陆的二维码 在浏览器中访问登陆接口 https://login.wx.qq.com/ 我们查找二维码的图片可以看到 其中 ...

  9. redis info

    redis命令详细文档可参考:http://redisdoc.com/index.html info命令显示redis详细的状态信息. 命令的基本用法有三种: 1)info:部分redis状态统计信息 ...

  10. Unable to preventDefault inside passive event listener

    最近做项目经常在 chrome 的控制台看到如下提示: Unable to preventDefault inside passive event listener due to target bei ...