算法总结—最近公共祖先1

news/2025/3/26 7:45:27/文章来源:https://www.cnblogs.com/LRRabcd/p/18789218

最近公共祖先定义

树上的两个点 \(a\)\(b\),它们的祖先中相同且深度最深的节点就是 \(a\)\(b\) 的最近公共祖。

求法

1.暴力

\(a\)\(b\) 的所有祖先都标记上,在从下往上找第一个两个都标记了的点。单次询问的时间复杂度 \(\mathcal{O}(dep_a+dep_b)\)

1.5.暴力优化

  • 如果 \(a\) 的深度 \(dep_a\)\(b\) 的深度 \(dep_b\) 大,让 \(a\) 不停的往上跳,直至 \(dep_a=dep_b\)

  • 如果 \(a\) 的深度 \(dep_a\)\(b\) 的深度 \(dep_b\) 小,让 \(b\) 不停的往上跳,直至 \(dep_a=dep_b\)

  • 如果 \(dep_a=dep_b\)\(a\)\(b\) 同时往上跳,直至 \(a=b\)

单次询问的时间复杂度 \(\mathcal{O}(dep_a+dep_b-2dep_{\text{LCA}})\)

2.倍增

预处理

预处理出 每个结点 \(i\) 的深度 \(dep_i\),和每个节点 \(i\) 往上走 \(2_j\) 步所到达的点 \(dp_{i,j}\)

\(dep\) 数组和好处理 \(dep_u=dep_{fa}+1\)。那么 \(dp\) 数组怎么转移呢,首先一个节点 \(i\) 往上跳 \(2_j\) 次等于 \(i\) 节点往上跳 \(2_{j-1}\) 所到达的节点往上再跳 \(2_{j-1}\) 次,所以 \(dp_{i,j}=dp_{dp_{i,j-1},j-1}\)\(dp_{i,0}=fa\)

预处理这一步的时间复杂度是 \(\mathcal{O}(n\log n)\)

查询

为了方便操作,可以先让 \(a\) 的深度更小:

if(dep[a]>dep[b]){swap(a,b);
}

再让 \(b\) 跳到和 \(a\) 同一深度,但不是一个节点一个节点去跳,而是利用 \(dp\) 数组跳,也就是对 \(dep_b-dep_a\) 进行二进制拆分:

for(int i=20;i>=0;i--){if(dep[dp[b][i]]>=dep[a]){y=dp[b][i];}
}

再次利用 \(dp\) 数组,让 \(a\)\(b\) 同时往上跳,但不可以重合,因为重合了只能保证是公共祖先,不能保证深度最深:

for(int i=20;i>=0;i--){if(dp[a][i]!=dp[b][i]){a=dp[a][i];b=dp[b][i];}
}

答案是 \(dp_{a,0}\)

单次询问的时间复杂度 \(\mathcal{O}(\log n)\)

【模板】最近公共祖先(LCA)

代码

#include<bits/stdc++.h>
using namespace std;
vector<int>G[500005];
int dep[500005];
int dp[500005][21];
void dfs(int u,int fa){dp[u][0]=fa;dep[u]=dep[fa]+1;for(int i=1;(1<<i)<=dep[u];i++){dp[u][i]=dp[dp[u][i-1]][i-1];}for(auto i:G[u]){int v=i;if(v==fa){continue;}dfs(v,u);}return;
}
int query(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=20;i>=0;i--){if(dep[dp[y][i]]>=dep[x]){y=dp[y][i];}}if(x==y){return x;}for(int i=20;i>=0;i--){if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}}return dp[x][0];
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n,q,s;cin>>n>>q>>s;for(int i=1;i<n;i++){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}dfs(s,0);while(q--){int x,y;cin>>x>>y;cout<<query(x,y)<<"\n";}return 0;
}

点的距离

先求出 \(x\)\(y\) 的最近公共祖先 \(LCA\),答案就是 \(x\)\(LCA\) 的距离加上 \(y\)\(LCA\) 的距离 \(dep_x-dep_{LCA}+dep_y-dep_{LCA}\)

代码

#include<bits/stdc++.h>
using namespace std;
vector<int>G[100005];
int dep[100005];
int dp[100005][21];
void dfs(int u,int fa){dp[u][0]=fa;dep[u]=dep[fa]+1;for(int i=1;(1<<i)<=dep[u];i++){dp[u][i]=dp[dp[u][i-1]][i-1];}for(auto i:G[u]){int v=i;if(v==fa){continue;}dfs(v,u);}return;
}
int query(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=20;i>=0;i--){if(dep[dp[y][i]]>=dep[x]){y=dp[y][i];}}if(x==y){return x;}for(int i=20;i>=0;i--){if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}}return dp[x][0];
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n;cin>>n;for(int i=1;i<n;i++){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}dfs(1,0);int q;cin>>q;while(q--){int x,y;cin>>x>>y;int lca=query(x,y);cout<<dep[x]-dep[lca]+dep[y]-dep[lca]<<"\n";}return 0;
}

Dis

先求出每个节点 \(i\) 到根节点的距离 \(dis_i\),然后求出 \(x\)\(y\) 的最近公共祖先 \(LCA\),答案就是 \(x\)\(LCA\) 的距离加上 \(y\)\(LCA\) 的距离 \(dis_x-dis_{LCA}+dis_y-dis_{LCA}\)

代码

#include<bits/stdc++.h>
using namespace std;
struct edge{int v,w;
};
vector<edge>G[10005];
int dep[10005];
int dp[10005][21];
int dis[10005];
void dfs(int u,int fa){dp[u][0]=fa;dep[u]=dep[fa]+1;for(int i=1;(1<<i)<=dep[u];i++){dp[u][i]=dp[dp[u][i-1]][i-1];}for(auto i:G[u]){int v=i.v;int w=i.w;if(v==fa){continue;}dis[v]=dis[u]+w;dfs(v,u);}return;
}
int query(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=20;i>=0;i--){if(dep[dp[y][i]]>=dep[x]){y=dp[y][i];}}if(x==y){return x;}for(int i=20;i>=0;i--){if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}}return dp[x][0];
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n,q;cin>>n>>q;for(int i=1;i<n;i++){int u,v,w;cin>>u>>v>>w;G[u].push_back({v,w});G[v].push_back({u,w});}dfs(1,0);while(q--){int x,y;cin>>x>>y;int lca=query(x,y);cout<<dis[x]-dis[lca]+dis[y]-dis[lca]<<"\n";}return 0;
}

祖孙询问

先求出 \(x\)\(y\) 的最近公共祖先 \(LCA\)

  • 如果 \(x=LCA\),则 \(x\)\(y\) 的祖先,输出 1

  • 如果 \(y=LCA\),则 \(y\)\(x\) 的祖先,输出 2

  • 否则 \(x\)\(y\) 没有任何关系输出 0

代码

#include<bits/stdc++.h>
using namespace std;
vector<int>G[100005];
int dep[100005];
int dp[100005][21];
void dfs(int u,int fa){dp[u][0]=fa;dep[u]=dep[fa]+1;for(int i=1;(1<<i)<=dep[u];i++){dp[u][i]=dp[dp[u][i-1]][i-1];}for(auto i:G[u]){int v=i;if(v==fa){continue;}dfs(v,u);}return;
}
int query(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=20;i>=0;i--){if(dep[dp[y][i]]>=dep[x]){y=dp[y][i];}}if(x==y){return x;}for(int i=20;i>=0;i--){if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}}return dp[x][0];
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n;cin>>n;int rt=0;for(int i=1;i<=n;i++){int u,v;cin>>u>>v;if(v==-1){rt=u;continue;}G[u].push_back(v);G[v].push_back(u);}dfs(rt,0);int q;cin>>q;while(q--){int x,y;cin>>x>>y;int lca=query(x,y);if(lca==x){cout<<1<<"\n";}else if(lca==y){cout<<2<<"\n";}else{cout<<0<<"\n";}}return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/904757.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【日记】油菜花即是春天!(913 字)

正文第一次睁眼,是九点多钟。朝哥没给我发消息,看来应该是不会上课了。翻身,又沉沉睡去。第二次睁眼,是下午一点多钟。起床,吃饭。吃完刚好两点钟。朝哥两点钟上课,正好离吃饭的地方不远,想去看看。走过一段斜坡,远远看到有一个四五岁的小孩子扒着玻璃门看。我就知道,…

20244104 实验一《Python程序设计》实验报告

20244104 2024-2025-2 《Python程序设计》实验x报告 课程:《Python程序设计》 班级:2441 姓名:陈思淼 学号:20244104 实验教师:王志强 实验日期:2025年3月23日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能; 3.编写程序,练习变…

一文(加代码示例)说透在线客服系统技术难点

我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户,时常有同行询问在线客服系统开发中的一些技术问题,在这篇文章中,我将从多个角度探讨在线客服系统的技…

CMS圣经:CMS垃圾回收器的原理、调优,多标+漏标+浮动垃圾 分析与 研究(图解+秒懂+史上最全)

本文的 原始地址 ,传送门 本文的 原始地址 ,传送门 尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:听说你是高手,说说,你的CMS怎么…

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

漏洞信息 Next.js 是一个基于 React 的流行 Web 应用框架,提供服务器端渲染、静态网站生成和集成路由系统等功能。包含众多功能,是深入研究复杂研究的完美游乐场。在信念、好奇心和韧性的推动下,我们出发去探索它鲜为人知的方面,寻找等待被发现的隐藏宝藏。 当使用中间件进…

【题解】洛谷P731[NOI1999] 生日蛋糕

前言:阅读理解+剪枝+头脑风暴 Designed By FrankWkd 遵循GNU GPL2.0开源协议。 题目 P1731 [NOI1999] 生日蛋糕 题目背景 数据加强版 link 题目描述 7 月 17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为 \(N\pi\) 的 \(M\) 层生日蛋糕,每层都是一个圆柱体。 设从下往上数…

【每日一题】20250324

在这个世界上,你做了什么不重要,重要的是让别人知道你做了什么。【每日一题】图中 \(a\),\(b\),\(c\),\(d\) 为四根与纸面垂直的长直导线,其横截面位于正方形的四个顶点上,导线中通有大小相同的电流,方向如图所示.一带正电的粒子从正方形中心 \(O\) 点沿垂直于纸面的方…

20242935 2024-2025-2 《网络攻防实践》第四周作业

20242935 2024-2025-2 《网络攻防实践》第四周作业 实践四 TCP/IP网络协议攻击 一、实验要求 在网络攻防实验环境中完成TCP/IP协议栈重点协议的攻击实验,包括ARP缓存欺骗攻击、ICMP重定向攻击、SYN Flood攻击、TCP RST攻击、TCP会话劫持攻击。 二、知识点梳理 (1)ARP病毒攻…

可视化图解算法:单链表的排序(排序链表)

对于链表的相关操作,我们总结了一套【可视化+图解】方法,依据此方法来解决链表相关问题,链表操作变得易于理解,写出来的代码可读性高也不容易出错。1. 题目 描述 给定一个节点数为n的无序单链表,对其按升序排序。 数据范围:0<n≤1000000 要求:时间复杂度 O(nlogn) 示…

火狐浏览器所有版本-历史版本

火狐浏览器所有版本: http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/IG:从这个路径,找到你想要的版本,然后进入 /win64 ,然后再进入 /zh-CN ,找到exe 下载安装就行。

PLM项目管理软件的定义、作用与发展趋势

PLM(Product Lifecycle Management)项目管理软件在现代企业的产品研发与管理过程中扮演着至关重要的角色。随着科技的飞速发展和市场竞争的日益激烈,企业对于产品全生命周期的有效管理需求愈发迫切,PLM项目管理软件应运而生并不断发展。它不仅仅是一款简单的工具,更是企业…