算法学习笔记【1】| LCA(最近公共祖先)
LCA(最近公共祖先)
Part 1:逐步上跳
假设u,v是所求的两点,先把深度大的点逐步上移直到深度相同。 然后两者同时上移,直到第一次遇到相同的点为止。
时间复杂度: O(n)<script type="math/tex;mode=inline" id="MathJax-Element-1">O(n)</script>O(n)
Part 2:倍增
考虑优化跳的过程。
定义anc[k][i],表示i向上第 2k<script type="math/tex;mode=inline" id="MathJax-Element-2">2^k </script>2^k 个祖先。
anc可以按照k为阶段动态规划得出,初始化为:anc[0][i]=fa[i];。
//Author: Velvet on Luogu(uid=443675)
void init(){
F(k,1,18){
F(i,1,n){
anc[k][i]=anc[k-1][anc[k-1][i]];
}
}
}
//Author: Velvet on Luogu(uid=443675)
int LCA(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
dF(i,18,0){
if(dep[anc[i][u]]>=dep[v]){
u=anc[i][u];
}
}
if(u==v) return u;
dF(i,18,0){
if(anc[i][u]!=anc[i][v]){
u=anc[i][u];v=anc[i][v];
}
}
return anc[0][u];
}
if(u==v) return u;,这句话很重要,没有的话会导致答案变成LCA的祖先。
至于anc可能超出树的大小的问题,可以这样理解:
如果anc[i][u]超出树的范围,dep[anc[i][u]]=0,必然不会赋值给u。
下面也是一样,anc都是0,不会赋值,保证答案的合法。
Part 3:Tarjan(离线算法)
Tarjan 是基于并查集的。
倍增法优化了向上跳这一步骤,Tarjan 算法则在第一次遍历整棵树的时候得到所有答案,因此需要先存储下每个询问,故该算法为离线算法。
假设 x,y 的LCA为 u。那么x,y必须是u的不同子树中的节点。
我们把每一次操作完后的子树和其父亲节点合并,这里使用并查集。
然后对于任意一个其他子树中的被提问节点,判断其询问的另一个节点有无访问记录,若是,记录答案为其并查集树根。
//Author: Velvet on Luogu(uid=443675)
void dfs(int now){
fa[now]=now;//并查集初始化
vis[now]=1;//访问记录
for(auto i:G[now]){
if(vis[i]) continue;
dfs(i);
fa[i]=now;//合并
}
for(auto i:Q[now]){//询问列表(i.first是另一个询问,i.second是询问的顺序
if(!vis[i.first]) continue;
ans[i.second]=find(i.first);
}
}
Part 4:树剖
先重链剖分这棵树。每次求LCA就不断跳重链,直到u,v在同一条重链上,答案就是u,v中深度小的。
向上跳重链时需要先跳所在重链顶端深度较大的那个。
//Author: Velvet on Luogu(uid=443675)
const int N=500005,M=(N<<1),inf=0x3f3f3f3f;
int n,m,s,siz[N],fa[N],hson[N],top[N],dep[N];
vector<int> G[N];
void dfs1(int now,int father){
siz[now] = 1;
for(auto i:G[now]){
if(i == father) continue;
dep[i] = dep[now] + 1;
dfs1(i,now);
siz[now] += siz[i];
fa[i] = now;
if(siz[hson[now]]<siz[i])
hson[now] = i;
}
}
void dfs2(int now,int t){
top[now] = t;
if(hson[now])
dfs2(hson[now],t);
for(auto i:G[now]){
if(i == hson[now]||i == fa[now]) continue;
dfs2(i,i);
}
}
int LCA(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
else u = fa[top[u]];
}
return dep[u] > dep[v] ? v : u;
}
int main(){
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m>>s;
F(i,1,n-1){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dep[s] = 1;
dfs1(s,-1);
dfs2(s,s);
F(i,1,m){
int u,v;
cin>>u>>v;
cout<<LCA(u,v)<<endl;
}
return 0;
}
模板题(on Luogu):SP14932,P3379
算法学习笔记【1】| LCA(最近公共祖先)的更多相关文章
- 算法学习笔记(5): 最近公共祖先(LCA)
最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...
- LCA近期公共祖先
LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...
- LCA 近期公共祖先 小结
LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...
- lca 最近公共祖先
http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- LCA(最近公共祖先)模板
Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
随机推荐
- centos7搭建postgresql主从(主备)架构
本篇介绍如何在centos7系统搭建一个postgresql主备集群实现最近的HA(高可用)架构.后续更高级的HA模式都是基于这个最基本的主备搭建. 节点规划 ip 主机名 用途 192.168.18 ...
- 走进Kaggle的未知领域:性别和年龄推断算法解析
1.环境设置: 此环节将加载实现笔记本无缝功能的基本模块,包括NumPy.Pandas和TensorFlow等库.此外,它还建立了关键的环境常数,如图像尺寸和学习率,这对后续分析和模型训练至关重要 ...
- Java常用编程类库
Java语言已经有许多非常成熟的开源基础类库,封装了日常开发中的各种常用操作,如:对象判空,字符串编码,本地缓存等等. 可以直接在项目中引入对应类库使用即可,或者参与完善相应类库的方法. 现将常用的基 ...
- geacon_pro配合catcs4.5上线Mac、Linux
最新最全文章见我个人博客: xzajyjs.cn 一些链接 Try师傅的catcs4.5项目: https://github.com/TryGOTry/CobaltStrike_Cat_4.5,最新版 ...
- Kotlin 协程四 —— Flow 和 Channel 的应用
目录 一. Flow 与 Channel 的相互转换 1.1 Flow 转换为 Channel 1.1.1 ChannelFlow 1.1.2 produceIn -- 将 Flow 转换为单播式 C ...
- 【Azure Developer】AAD API如何获取用户“Block sign in”信息(accountEnabled)
问题描述 使用API获取所有Azure AD中的用户列表,API所参考的文档:https://docs.microsoft.com/en-us/graph/api/user-list?view=gra ...
- Java 四种不同的权限修饰
private 私有属性 只在同一个包下 同一个类中可以调用 同一个包下,不同的类中,可以调用 缺省,保护(protected),公共(public)可以调用, 不同的包下的类中,继承关系,可以调用 ...
- 快速复习JDBC(超详细)
第一章 JDBC概述 之前我们学习了JavaSE,编写了Java程序,数据保存在变量.数组.集合等中,无法持久化,后来学习了IO流可以将数据写入文件,但不方便管理数据以及维护数据的关系: 后来我们学 ...
- mybatis查询大批量数据的几种方式
问题背景 公司里有很多需要跑批数据的场景,这些数据几十万到几千万不等,目前我们采用的是分页查询,但是分页查询有个深度分页问题,上百万的数据就会查询的很慢 常规解决方案 全量查询 分页查询 流式查询 游 ...
- 摆脱鼠标系列 vscode 向右拆分编辑器 ctrl + 右箭头
摆脱鼠标系列 vscode 向右拆分编辑器 ctrl + 右箭头 为什么 今天看见一个两栏工作的,左侧放的是目录大纲,右侧是代码内容 用快捷键 ctrl + 右箭头 快速扩展一个,关闭可以ctrl + ...