LCA学习笔记
写在前面
目录
题目完成度
一、LCA的定义
LCA指的是最近公共祖先。具体地,给定一棵有根树,若结点z既是结点x的祖先,又是结点y的祖先,则称z是x,y的公共祖先。在x,y的公共祖先中,深度最大的一个结点称为x,y的最近公共祖先,记为LCA(x,y)
二、暴力法求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;
}
三、倍增法求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 ;
}
代码戳这里
四、树链剖分求LCA
咕咕咕咕咕
五、LCA典型例题
咕咕咕咕
LCA学习笔记的更多相关文章
- 倍增LCA学习笔记
前言 "倍增",作为一种二进制拆分思想,广泛用于各中算法,如\(ST\)表,求解\(LCA\)等等...今天,我们仅讨论用该思想来求解树上两个节点的\(LCA\)(最近公共祖先 ...
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- 树链剖分 树剖求lca 学习笔记
树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...
- kruskal重构树学习笔记
\(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal\) 求最小(大)生成树,树上求 \(lca\). 算法详 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- 「学习笔记」wqs二分/dp凸优化
[学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
随机推荐
- Jenkins之Job建立-运行本地脚本
新建一个自由风格的项目,运行本地脚本 1.点击菜单栏中的“新任务” 2.进入该页面后输入一个项目名称,然后选择“构建一个自由风格的软件项目”,滑动到最底端,点击ok(在左下角) 3.进入下图页面后 “ ...
- Python编码规范(PEP8)及奇技淫巧(不断更新)
https://blog.csdn.net/MrLevo520/article/details/69155636
- 基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)
基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势) 前言 前几天对Apollo配置中心的demo进行一个部署试用,现公司已决定使用,这两天进行分布式部署的时候 ...
- python3 闭包函数
'''闭包函数:内部函数引用外部函数变量(非全局变量)'''def func(y): x = 1 def func1(): print(x, y) return func1 f = func(2)pr ...
- jeecg字典表—普通表
创建普通表 同步数据库(创建对应的表) 验证功能效果 添加用户表,并添加对应的级别属性 同步用户表 字典功能测试 然后生成最新代码,添加到菜单即可
- JS 面向对象 ~ 继承的7种方式
前言: 继承 是 OO 语言中的一个最为人津津乐道的概念.许多 OO 语言都支持两种继承方式:接口继承 和 实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在 ...
- [转帖]Windows 10 部分早期版本已完全停止技术支持服务
Windows 10 部分早期版本已完全停止技术支持服务 2019-4-12 01:27| 发布者: cjy__05| 查看: 10186| 评论: 47|来自: pcbeta 收藏分享 转帖来源:h ...
- Zabbix 3.0的前端默认在Centos 6上不支持
由于PHP版本的原因,RHEL 6上的Zabbix前端不受支持. 由于Zabbix 3.0的要求是PHP 5.4.0或更高版本,而RHEL 6最新版本是5.3.3. 在大多数情况下,Zabbix服务器 ...
- 为什么String被设计为不可变?是否真的不可变?
1 对象不可变定义 不可变对象是指对象的状态在被初始化以后,在整个对象的生命周期内,不可改变. 2 如何不可变 通常情况下,在java中通过以下步骤实现不可变 对于属性不提供设值方法 所有的属性定义为 ...
- Linux之文本编辑器Vim
一.什么是vim vi是一种模式编辑器.vi 是Unix世界里极为普遍的全屏幕文本编辑器,几乎可以说任何一台Unix机器都会提供这套软体,其他的文本编辑器则不一定会存在,但是目前我们使用比较多的是 v ...