【模板】最近公共祖先:LCA算法
LCA最近公共祖先
要求 \ 给出一个树和他的根节点\text{root} \quad给出Q个询问 回答\text {LCA}(a,b)
\end{align}
\]
给出一个表格
| 最近公共祖先 | 朴素算法 | 倍增算法 | Tarjan算法 | 树链剖分 |
|---|---|---|---|---|
| 数据结构 | fa[u],dep[u] | fa[u] [v], dep[u] | query[u], ans[i], vis[u] | fa[u], dep[u], size_[u], son[u], top[u] |
| 算法 | 暴力 | 倍增法 | 并查集 | 重链剖分 |
| 做法 | 暴力一次跳跃 | 深搜打表,跳跃查询 | 深搜,回时指父,离时搜根 | 两遍深搜打表,跳跃查询 |
| 时间复杂度 | O(n^2) | O(n + mlogn) | O(n+m) | O(n + mlogn) |
朴素版本
& \mathrm{LCA}朴素版\quad查询复杂度:O(n) \\
& 一次一次往上跳\\
& 让u是更深的节点\quad然后把u跳到和v一样的高度\\
& 然后u,v同时往上跳 当u==v时就是答案
\end{align*}
\]
#include <cstdio>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#define ep emplace_back
#define lld long long
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);
#define vec vector
const int N = 2e6+9;
const int INF = 0x7FFFFFFF; //2147483647
const int inf1 = 0x3f3f3f3f; //1061109567
const int inf2 = 0x7f7f7f7f; //2139062143 memset赋值用
using namespace std;
int head[N],idx=0;
struct node{
int to,val,next;
};
node e[N<<1];
int fa[N];
void add(int u ,int v, int val){
e[idx] = {v,val,head[u]};
head[u] = idx++;
}
int a[N],b[N];
int n,root,Q;
int depth[N],max_dep=-1;
int pa[N];
vec<int>child[N];
void bd(){
cin>>n>>Q>>root;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;++i){
int u,v;
cin>>u>>v;
add(u,v,0);
add(v,u,0);
//树是双向边
}
}
void dfs(int u){
for(int i=head[u] ; i!=-1 ; i=e[i].next){
int v = e[i].to;
if(v == fa[u]) continue;
fa[v] = u;
depth[v] = depth[u]+1;
max_dep = max(max_dep,depth[v]);
dfs(v);
}
}
int LCA(int u,int v){
//朴素版LCA 一次一次向上跳
if(depth[u] < depth[v])
swap(u,v);
//默认最深的点是u
while(depth[u] != depth[v]){
u = fa[u];
//先把u往上跳
//u的深度和v一样
}
while( u != v){
u=fa[u];
v=fa[v];
//u,v深度一样之后只需要把u v一起往上跳跃
//当u==v 时 这个时候的u==v ==LCA(u,v)
}
return u;
}
int main(){
ios;
bd();
depth[root] = 0;
fa[root] = -1;
dfs(root);
for(int i=1;i<=Q;++i){
int a,b;
cin>>a>>b;
cout<<LCA(a,b)<<"\n";
}
return 0;
}
倍增加速算法版本
& 在线版本\to倍增法\quad n = \sum_{k=0} ^{\log_2n}2^{k}\\
& 不断分解查询的步骤\\
& 即2^k = 2^{k-1}+2^{k-1}\quad \\
&令u=\mathrm{anc}(i,j-1) ,\mathrm{anc}(i,j) = \mathrm{anc}(u,j-1)\\
&然后通过\text{dfs}记录每个点的\text{depth}和他的父亲节点\\
&需要注意的问题是可能会跳过头,因此需要跳到那个最离v最近的地方\\
&然后同时把u,v向上跳 u,v的\text{anc}(u,0)就是\text{LCA}(u,v)
\end{align}
\]
#include <cstdio>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#define ep emplace_back
#define lld long long
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);
#define vec vector
const int N = 5e5+9;
const int INF = 0x7FFFFFFF; //2147483647
const int LOG = log2(N)+1;
const int inf1 = 0x3f3f3f3f; //1061109567
const int inf2 = 0x7f7f7f7f; //2139062143 memset赋值用
using namespace std;
int n,Q,root;
int logn;
int anc[N][LOG];
//anc(i,j):i号节点的第2^(j-1)个祖先节点
int depth[N],max_depth=0;
int head[N],idx=0;
bool vis[N];
struct node{
int to,val,next;
};
node e[N<<1];
void add(int u,int v,int val){
e[idx] = {v,val,head[u]};
head[u] = idx++;
}
void bd(){
cin>>n>>Q>>root;
logn = log2(n);
memset(head,-1,sizeof head);
for(int i=1; i<=n-1 ;++i){
int u,v,val;
cin>>u>>v;
add(u,v,0);
add(v,u,0);
}
}
void dfs(int u,int fa){
anc[u][0] = fa;
//2^(0)=1 v的一号祖先就是父亲
for(int i=head[u] ; i!=-1 ; i=e[i].next){
int v = e[i].to;
if( v==fa ) continue;
//双向边会跑到父亲 故跳过
depth[v] = depth[u]+1;
dfs(v,u);
}
}
void init(){
//预处理dfs后的所有祖先
//pow(2,k) = pow(2,k-1) + pow(2,k-1)
//需要用到j-1 故j从1开始
for(int j = 1 ; j<=logn ;++j){
for(int i=1 ; i<=n ; ++i){
int v = anc[i][j-1];
anc[i][j]= anc[v][j-1];
}
}
}
int LCA(int u,int v){
if(depth[v] > depth[u])
swap(u,v);
//让u是最深的节点
/*
for(int i=logn ; i>=0 ; --i){
if(depth[anc[u][i]] >= depth[v]){
u = anc[u][i];
}
}
*/
for(int i=logn ; i>=0 ; --i ){
if( depth[u]-(1<<i) >= depth[v] ){
u = anc[u][i];
}
}
if(u == v) return u;//提前判定一次
for(int i=logn ; i>=0 ; --i ){
if(anc[u][i] != anc[v][i]){
u= anc[u][i];
v= anc[v][i];
}
}
return anc[u][0];
}
int main(){
ios;
bd();
dfs(root,-1);
init();
for(int i=1 ; i<=Q ; ++i){
int u,v;
cin>>u>>v;
cout<<LCA(u,v)<<"\n";
}
return 0;
}
Tarjan算法
sssxxx
这是树链剖分算法
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#define lld long long
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);
using namespace std;
const int N =5e5+9;
int head[N],idx=0;
bool vis[N];
int n,m,Q,root;
int dep[N],fa[N],top[N],size_[N],son[N];
vector<int>e[N<<1];
void bd(){
cin>>n>>Q>>root;
memset(head,-1,sizeof(head));
for(int i=1 ; i<=n-1 ; ++i){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
}
void dfs1(int u,int fa_){
fa[u] = fa_;
dep[u] = dep[fa_]+1;
size_[u] = 1;
for(int v:e[u]){
if(v==fa_) continue;
dfs1(v,u);
size_[u] = size_[u] + size_[v];
if(size_[son[u]] < size_[v]){
son[u] = v;
}
}
}
void dfs2(int u,int topu){
//topu:u这条链上的顶点
top[u] = topu;
//没有重儿子说明是叶子节点 结束dfs
if(son[u]==0) return;
dfs2(son[u],topu);
//先记录每个点的topu 再更新
for(int v:e[u]){
//上面的dfs搜的就是重儿子son[u] 这里不重复搜了跳过
if(v==fa[u] || v==son[u]) continue;
//剩下的就是轻儿子 轻儿子都是叶子节点 单独成链 这时候端点就是自己
dfs2(v,v);
}
}
int LCA(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])
swap(u,v);
u = fa[top[u]];
}
//当他们在同一条重链上时结束循环
//深度小的哪个点就是LCA
return dep[u]<dep[v]?u:v;
}
int main(){
ios;
bd();
dfs1(root,-1);
dfs2(root,root);
while(Q--){
int a,b;
cin>>a>>b;
cout<<LCA(a,b);
cout<<"\n";
}
return 0;
}
【模板】最近公共祖先:LCA算法的更多相关文章
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- POJ 1330 Nearest Common Ancestors 【最近公共祖先LCA算法+Tarjan离线算法】
Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 20715 Accept ...
- 算法学习笔记(5): 最近公共祖先(LCA)
最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...
- LCA(最近公共祖先)算法
参考博客:https://blog.csdn.net/my_sunshine26/article/details/72717112 首先看一下定义,来自于百度百科 LCA(Lowest Common ...
- 【并查集】【树】最近公共祖先LCA-Tarjan算法
最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- 【洛谷 p3379】模板-最近公共祖先(图论--倍增算法求LCA)
题目:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 解法:倍增. 1 #include<cstdio> 2 #include<cstdlib> 3 #include ...
随机推荐
- #Powerbi 1分钟学会,SUMMARIZE函数,分组汇总并新建表
今天我们来学习一个新的表函数,SUMMARIZE函数是DAX中的一个函数,它可以根据一列或多列对数据进行分组,并且可以使用指定的表达式为汇总后的表添加新列,形成一张新表. 一:基础语法 SUMMARI ...
- oppo、一加 android14 chrome116内核 input @click不触发
// 兼容Chrome内核116及以上版本中配置disabled的input组件无法触发并冒泡click事件 .uni-input-input:disabled { pointer-events: n ...
- 利用 device_map、torch.dtype、bitsandbytes 压缩模型参数控制使用设备
为了更好的阅读体验,请点击这里 device_map 以下内容参考 Huggingface Accelerate文档:超大模型推理方法 在 HuggingFace 中有个重要的关键字是 device_ ...
- Linux 内核:设备驱动模型(1)sysfs与kobject基类
Linux 内核:设备驱动模型(1)sysfs与kobject基类 背景 学习Linux 设备驱动模型时,对 kobject 不太理解.因此,学习了一下. 现在我知道了:kobj/kset是如何作为统 ...
- Android Verified Boot介绍与有关使用
Android Verified Boot介绍与有关使用 背景 在搞安卓驱动调试的时候,由于不熟悉,导致系统没有按照我预期启动完毕:因此需要注意这一块的东西. 简介 Verified Boot 是 A ...
- 从 Dict 转到 Dataclass
从 dataclass 转到 dict 可以用 asdict 函数 , 反向转换的时候 就比较困难. 不用外部的包的情况下, 提供一种思路. def mask(v, d): #v 是 dict 数据, ...
- Linux如何安装PHPMyAdmin
1,我们要以root帐号登入 . 2,PHP支持模块安装.在CentOS操作系统安装完毕后,其实PHP支持模块并没有安装上去,如果想使用PhpMyAdmin,首先需要安装PHP支持模块,我们需要两个P ...
- django 信号第一个 raise ValidationError出现后,还会继续下一个if吗
在你提供的代码片段中,如果第一个 raise ValidationError 触发,会抛出异常并停止执行当前函数或代码块.这是因为异常(Exception)会中断正常的代码流程,将控制权传递给调用堆栈 ...
- 关于导入react native项目导致运行异常的那些事
从git上导入公司的项目,在本地运行的时候,项目无法运行.sdk,jdk,node都是使用公司规定的版本,项目中的local.properties文件sdk.dir路径也换成了自己本地的目录,结果就在 ...
- Windows/Linux 安装NVM及npm配置
nvm安装及npm配置 nvm nvm下载 Windows: Releases · coreybutler/nvm-windows (github.com) Windows直接下载相应版本的nvm-s ...