算法学习笔记【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\)算法.这些算法可以快 ...
随机推荐
- Linux下SQLPLUS替代工具rlwrap安装使用
rlwrap工具可以解决linux下sqlplus 提供浏览历史命令行的功能,和删除先前输入错误的字母等问题 1.安装 需要readline包 这个安装光盘就有 [root@asm RedHat]# ...
- MySQL数据库备份与恢复方法
MySQL数据库备份与恢复方法 mysql -uroot -p show databases; 1.导出数据库 1).MySQL命令行导出整个数据库(包含数据) 导出文件默认是存在mysql\bin目 ...
- Android使用SurfaceView实现签名板
SurfaceView使用 首先创建一个SurfaceViewSign类,继承SurfaceView类,继承 SurfaceHolder.Callback和Runnable接口,代码如下: impor ...
- OpenResty中如何实现,按QPS、时间范围、来源IP进行限流
OpenResty是一个基于Nginx与Lua的高性能Web平台,它通过LuaJIT在Nginx中运行高效的Lua脚本和模块,可以用来处理复杂的网络请求,并且支持各种流量控制和限制的功能. 近期研究在 ...
- 【复盘#01】myh笔试
存疑 1.http响应体中版本和缓存是哪个字段(Etga) http和https的区别 2.mysql同一个表中有多个相同字段但搜索的时候只搜得出某一个,要怎么修改(inner ..) mysql如何 ...
- 【算法day6】哈希表、有序表、链表(反转单链表)
哈希表的简单介绍 1)哈希表在使用层面上可以理解为一种集合结构 2)如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet) 3)如果既有key,又有伴 ...
- 《Similarity-based Memory Enhanced Joint Entity and Relation Extraction》论文阅读笔记
代码 原文 摘要 文档级联合实体和关系抽取是一项难度很大的信息抽取任务,它要求用一个神经网络同时完成四个子任务,分别是:提及检测.共指消解.实体分类和关系抽取.目前的方法大多采用顺序的多任务学习方式, ...
- 【Azure Function App】在VS Code中,创建好Function App后部署到Azure中,无法选择Subscriptions
问题描述 在VS Code中,创建好Function App后部署到Azure中,无法选择Subscriptions 问题解答 对于无法使用 VS Code 部署 Function App 到 Azu ...
- ASP.NET Core 从入门到精通-资源收集导航
ASP.NET Core 从入门到精通-资源收集导航 目录 ASP.NET Core 从入门到精通-资源收集导航 学习路线 学习路线资源导航大全 1,介绍 2,入门 3,教程 创建 Razor 页面 ...
- Android Studio 有关 setOnClickListener() 方法的总结
•前言 在 Android Studio 开发中,你会经常和这种代码打交道: 1 package com.example.activitytest; 2 public class FirstActiv ...