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(最近公共祖先)的更多相关文章

  1. 算法学习笔记(5): 最近公共祖先(LCA)

    最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...

  2. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  3. LCA近期公共祖先

    LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...

  4. LCA 近期公共祖先 小结

    LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...

  5. lca 最近公共祖先

    http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...

  6. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  7. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  8. LCA(最近公共祖先)模板

    Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...

  9. CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )

    CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...

  10. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

随机推荐

  1. 之前练手使用基于gin的go web项目

    目录结构: `-- demo |-- cmd | |-- api.go | `-- root.go |-- common | `-- consts | `-- consts.go |-- config ...

  2. 一次nginx返回422状态码的经历

    故事背景 后端使用Docker Compose部署一个代码片段管理应用:snibox,某天因为云服务卡死重启之后再次访问时,登录或退出都返回422状态码. 界面提示如下: 不过奇怪的是:直接通过IP+ ...

  3. java获取kafka consumer lag、endOffsets、beginningOffsets以及 KafkaConsumer总结

    一.java获取kafka consumer lag.endOffsets.beginningOffsets maven依赖: <dependency> <groupId>or ...

  4. Jmeter Xpath提取器你了解多少?

  5. ie7bug,一个块级元素或者一个行内元素里面有个子元素设置向右边浮动,在IE7会出现元素下掉现象!

    元素包含结构如下: <h1>testing<span>综合评级:<em></em></span></h1> CSS,如下 h1{ ...

  6. .net中最简单的http请求调用(比如调用chatgpt的openAI接口)

    支持.Net Core(2.0及以上)/.Net Framework(4.5及以上),可以部署在Docker, Windows, Linux, Mac. http请求调用是开发中经常会用到的功能,因为 ...

  7. Edge 语音识别 生成文字 显示在input new webkitSpeechRecognition()

    Edge 语音识别 生成文字 显示在input new webkitSpeechRecognition() 代码 <html> <head> <style> bod ...

  8. [SCOI 2009] 迷路 (矩阵快速幂)

    [SCOI 2009]迷路 传送门 问题描述 Windy 在有向图中迷路了. 该有向图有 \({N}\) 个节点,Windy 从节点 \({1}\) 出发,他必须恰好在 \({T}\) 时刻到达节点 ...

  9. "高绩效"指南

    前言 最近被问到一个问题,在工作中,如何拿高绩效.或者换一种表达方式,如何成为老板的"嫡系". 在这里我想简单谈一谈我的想法,可能不准确,各位看官,当饭后茶语罢了. 为了更加有说服 ...

  10. Linux 使用 selenium 环境配置

    1.需要安装 Chrome 浏览器 yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64 ...