算法学习笔记(LCA)

L C A LCA LCA:树上两个点的最近公共祖先。(两个节点所有公共祖先中,深度最大的公共祖先)

L C A LCA LCA的性质:

  1. 在所有公共祖先中, L C A ( x , y ) LCA(x,y) LCA(x,y) x x x y y y的距离都最短。
  2. x x x y y y之间最短的路径经过 L C A ( x , y ) LCA(x,y) LCA(x,y)
  3. x x x y y y本身也可以是它们自己的公共祖先。若 y y y x x x的祖先,则有 L C A ( x , y ) = y LCA(x,y) = y LCA(x,y)=y
  4. L C A ( x , y , z ) = L C A ( x , L C A ( y , z ) ) LCA(x,y,z) = LCA(x, LCA(y,z)) LCA(x,y,z)=LCA(x,LCA(y,z))
  5. L C A ( x 1 , x 2 , . . . , x n ) = L C A ( d f s 序最大点, d f s 序最小点 ) LCA(x_1, x_2,...,x_n) = LCA(dfs序最大点,dfs序最小点) LCA(x1,x2,...,xn)=LCA(dfs序最大点,dfs序最小点)

本文主要介绍求 L C A LCA LCA的两种方法:倍增法和 T a r j a n Tarjan Tarjan

先引入例题:

【模板】LCA

题目描述

给定一个 n n n个点以结点 1 1 1为根的树,有 q q q次询问,每次询问给出两个点 u , v u,v u,v,求 L C A ( u , v ) LCA(u,v) LCA(u,v)

L C A ( u , v ) LCA(u,v) LCA(u,v)表示 u , v u,v u,v的最近公共祖先。

输入描述

第一行一个整数 n n n表示结点个数。 ( 1 ≤ n ≤ 2 × 1 0 5 ) (1 \leq n \leq 2 \times 10^5) (1n2×105)

第二行 n − 1 n−1 n1个整数,表示 2 ∼ n 2∼n 2n结点的父亲。

第三行一个整数 q q q,表示询问次数。 ( 1 ≤ q ≤ 2 × 1 0 5 ) (1 \leq q \leq 2 \times 10^5) (1q2×105)

接下来 q q q行,每行两个整数 u , v u,v u,v ( 1 ≤ u , v ≤ n ) (1≤u,v≤n) (1u,vn)

输出描述

对于每次询问,一行一个整数表示结果。

输入样例1

5
1 1 2 3
3
1 2
2 4
1 5

输出样例1

1
2
1

倍增法:

大体就是让两个点不断向上走,直到走到最近的相同的点。如何快速地走?这里就需要用倍增法来实现。倍增法的原理利用了二进制的性质:任意一个数字都可以由一个或几个 2 2 2的幂相加得到,所以可以以 1 , 2 , 4 , 8... 1,2,4,8... 1,2,4,8...这种 2 2 2的幂作为一步的长度向上走。

因为我们不知道应该走多大,所以应该先尝试走大数,能走即走,再尝试走小步。

为了快速地进行跳跃,我们需要预处理出一个帮助我们进行跳跃的数组 f a [ N ] [ 20 ] fa[N][20] fa[N][20] f a [ x ] [ i ] fa[x][i] fa[x][i]表示从 x x x出发向上走 2 i 2^{i} 2i步到达的位置)。计算 f a fa fa数组时用到了 d p dp dp的思路,这里有个递推公式: f a [ x ] [ i ] = f a [ f a [ x ] [ i − 1 ] ] [ i − 1 ] fa[x][i] = fa[fa[x][i - 1]][i - 1] fa[x][i]=fa[fa[x][i1]][i1],意思是从点 x x x出发,先跳 2 i − 1 2 ^ {i - 1} 2i1步,再跳 2 i − 1 2 ^ {i - 1} 2i1步。由小推到大。

得到这个数组之后,就可以快速地计算 L C A LCA LCA了。具体步骤如下:

先将点 x x x和点 y y y处理至同一深度。让深度较大的数以祖先链上深度与深度较小的那个点相等的点为目标进行跳跃。特殊的,如果该点就是深度较浅的那个点,则使用上述 L C A LCA LCA的性质中的第三条。

之后,点 x x x和点 y y y再同时向上跳跃。直至分别跳到 L C A ( x , y ) LCA(x,y) LCA(x,y)的两个儿子上。即 x ≠ y x \not = y x=y并且 f a [ x ] [ 0 ] = f a [ y ] [ 0 ] fa[x][0] = fa[y][0] fa[x][0]=fa[y][0]

最后得到的 f a [ x ] [ 0 ] fa[x][0] fa[x][0]就是 L C A ( x , y ) LCA(x,y) LCA(x,y)

时间复杂度为: O ( n l o g 2 n + q l o g 2 n ) O(nlog_2n + qlog_2n) O(nlog2n+qlog2n)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9, T = 20;vector<int> g[N];
int fa[N][30], dep[N];void dfs(int x)
{dep[x] = dep[fa[x][0]] + 1;     //预处理各个节点深度//预处理fa数组for(int i = 1; i <= T; ++i) fa[x][i] = fa[fa[x][i - 1]][i - 1];for(const auto &i : g[x]){dfs(i);}
}int lca(int u, int v)
{if(dep[u] < dep[v]) swap(u, v);     //不妨设点u深度大于等于点v//跳跃,先走大步再走小步//将u点跳至于点v相同高度for(int i =  T; i >= 0; --i){if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];}if(u == v) return u;//一起往上跳for(int i = T; i >= 0; --i){if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];}return fa[u][0];
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n; cin >> n;for(int i = 2; i <= n; ++i){cin >> fa[i][0];g[fa[i][0]].push_back(i);}dfs(1);int q; cin >> q;while(q--){int u, v; cin >> u >> v;cout << lca(u, v) << '\n';}return 0;
}

Tarjan

大概流程:

还是采用 d f s dfs dfs,访问到一个点的时候,先标记被访问,然后向下处理它的儿子节点,看儿子节点是否为所求 L C A LCA LCA有关的点,处理完儿子节点之后回溯上来时再处理自己,看自己是否为所求 L C A LCA LCA有关的点。再向上合并,合并采用并查集进行合并且使用路径压缩。

因为有多组询问,所以需要将询问离线。

对于一对点中的一个点处理的时候,若另一个点已经被访问过了,那么那个点在的并查集中的根节点就是它们的 L C A LCA LCA,这和 d f s dfs dfs的性质有关。都访问到的时候,它们一定在同一棵子树上,并且这颗子树的根显然就是它们的 L C A LCA LCA

时间复杂度为: O ( n + q ) O(n + q) O(n+q)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;int pre[N], dep[N], ans[N], fa[N];
vector<int> g[N];
vector<pair<int, int>> Q[N];
bitset<N> vis;//并查集基础操作
int find(int x)
{return pre[x] = (pre[x] == x ? x : find(pre[x]));
}void merge(int x, int y)    //将深度大的向深度小的合并,向上合并
{int fx = find(x), fy = find(y);if(dep[fx] < dep[fy]) swap(fx, fy);pre[fx] = fy;
}void dfs(int x)
{//顺手计算深度,合并时使用dep[x] = dep[fa[x]] + 1;vis[x] = true;for(const auto &y : g[x]) dfs(y);   //先处理所有儿子for(const auto &[y, id] : Q[x])     //在处理自己{if(!vis[y]) continue;       //如果对方未被访问就跳过ans[id] = find(y);}merge(x, fa[x]);
}void solve()
{int n; cin >> n;for(int i = 1; i <= n; ++i) pre[i] = i;for(int i = 2; i <= n; ++i){cin >> fa[i];g[fa[i]].push_back(i);}int q; cin >> q;//将询问离线for(int i = 1; i <= q; ++i){int x, y; cin >> x >> y;Q[x].push_back({y, i});Q[y].push_back({x, i});}dfs(1);for(int i = 1; i <= q; ++i) cout << ans[i] << '\n';
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ = 1;while(_--) solve();return 0;
}

L C A LCA LCA可以求树上两点之间的最短距离,也可以做树上差分,这里就不过多赘述。

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

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

相关文章

什么是AOT,AOT 有什么优点

JDK 9 引入了一种新的编译模式 AOT (Ahead of Time Compilation)。与 JIT (Just-In-Time Compilation) 不同&#xff0c;AOT 在程序执行前将其编译成机器码&#xff0c;属于静态编译。这种模式具有很多优点&#xff0c;但也有一些限制。本文将详细探讨 AOT 的优点以及其限制。 …

六西格玛培训证书攻略2024:一站式解决方案助你快速上手

目前&#xff0c;企业对于员工的专业能力和综合素质要求越来越高。六西格玛作为一种先进的质量管理方法&#xff0c;已经成为众多企业提升运营效率、降低成本的重要手段。张驰咨询针对2024年六西格玛培训证书考取&#xff0c;为广大学员制定了实用的攻略&#xff0c;帮助学员们…

玩转大模型 企业AI着陆新正解 神州问学AI原生赋能平台正式发布

在人工智能技术日新月异的今天&#xff0c;神州数码凭借深厚的行业洞察和技术积累&#xff0c;揭开了AI原生赋能平台——神州问学的神秘面纱。作为企业AI着陆的加速引擎&#xff0c;神州问学致力于通过AI原生场景赋能&#xff0c;为企业开辟一条通往智能未来的坦途。 神州问学—…

vue3使用el-radio-group获取表格数据无法选中问题

这里是引用 今天写项目发现使用el-radio-group无法获取表格中的数据&#xff0c;于是去官网查看了一下&#xff0c;发现写的没啥问题&#xff0c;就是 <el-radio value"1" size"large"> 未知</el-radio>这样的写法&#xff0c;又在网上看了一些…

不写一行代码,使用ChatGpt开发一个射击游戏

1.简介 最近需要开发一个网页应用&#xff0c;想到了使用ChatGpt生成Html页面&#xff0c;生成的效果非常好&#xff0c;说几句话就可以实现复杂的功能。不过需要一步步耐心的引导。然后我就想到可以用ChatGpt生成一个网页游戏。这个游戏包含了人物移动、跳跃、射击、生命值&a…

02Django项目安装和环境变量设置

凯哥英语视频 Django项目安装和环境变量设置 凯哥英语视频1.汉化1.打开PyCharm&#xff0c;点击File&#xff0c;再点击Settings2.然后点击 Plugins&#xff0c;再Marketplace&#xff0c;找到Chinese&#xff08;simplified&#xff09;Language&#xff0c;再点击Install然后…

linux给history查看历史执行命令加上日期和时间

问题&#xff1a; 打开linux&#xff0c;执行history看一下上次执行的命令&#xff0c;结果命令是显示出来了&#xff0c;但没有日期和时间。 解决办法&#xff1a; 编辑 ~/.bashrc vim ~/.bashrc 在末尾加入以下命令 export HISTTIMEFORMAT"%F %T " 保存并退…

Rust语言实现图像编码转换

一、概述 Rust 作为一门现代的系统编程语言&#xff0c;不仅性能出色&#xff0c;安全性高&#xff0c;而且生态系统也在不断成熟。在图像处理方面&#xff0c;image-rs库是 Rust 社区中广泛使用的一个开源库&#xff0c;它提供了丰富的图像编解码功能。今天&#xff0c;我将带…

GPT-4o: 从最难的“大海捞针”基准看起

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在阅读过程中有些知识点存在盲区&#xff0c;可以回到如何优雅的谈论大模型重新阅读。另外斯坦福2024人工智能报告解读为通识性读物。若对于如果…

Python slice() 使用方法及示例说明

slice()参数 slice() 可以采用三个参数&#xff1a; start&#xff08;可选&#xff09; -对象切片开始的起始整数。如果未提供或者值为None&#xff0c;则默认为第一个数据。 stop-整数&#xff0c;直到切片发生。切片在索引stop-1&#xff08;最后一个元素&#xff09;处停…

基于EBAZ4205矿板的图像处理:12二值化图像的膨胀与腐蚀

基于EBAZ4205矿板的图像处理&#xff1a;12二值化图像的膨胀与腐蚀 先看效果 注意&#xff1a;我的项目中的膨胀和腐蚀是对二值化图像中的像素值为255的像素进行处理&#xff0c;而我的图像中255为白色&#xff0c;0为黑色&#xff0c;所以是对颜色为白色的像素点进行的膨胀和…

【Vue】Vue 中的数据传递策略:探索跨组件通信的多样化方法

Vue 中的数据传递策略&#xff1a;探索跨组件通信的多样化方法 在现代的前端开发过程中&#xff0c;Vue.js 以其灵活和易于理解的结构脱颖而出&#xff0c;成为了广受欢迎的 JavaScript 框架之一。在构建动态应用时&#xff0c;组件之间的数据传递是必不可少的&#xff0c;但随…

Google I/O 2024:有关AI的一切已公布|TodayAI

2024年谷歌I/O大会圆满落幕&#xff0c;谷歌在会上发布了一系列更新&#xff0c;涵盖从最新的人工智能技术到Android系统的多项改进。此次大会特别关注于谷歌的Gemini人工智能模型&#xff0c;并详细介绍了这些模型如何被融入到Workspace、Chrome等多个应用程序中&#xff0c;展…

男士内裤哪个牌子质量好又舒服?五款不容错过的男士内裤

男士内裤&#xff0c;作为男士日常穿着的重要贴身衣物&#xff0c;其舒适度和透气性至关重要。尽管有些男士可能习惯长时间穿着同一条内裤&#xff0c;但为了确保健康和舒适&#xff0c;建议每3-6个月更换一次内裤。长时间不更换内裤会导致其舒适性和透气性下降&#xff0c;同时…

性价比王者HUSB237,极简PD Sink的“瘦身秘籍”

在小型化、高集成的要求下&#xff0c;慧能泰取电芯片进行技术升级后“瘦身成功”&#xff0c;推出最新一代极具性价比的最简PD Sink取电芯片——HUSB237。 图1&#xff1a;HUSB237 demo及封装图 HUSB237 是一款极具性价比的最简PD Sink取电芯片&#xff0c;支持PD3.1协议包含…

C#知识|上位机面向对象编程时如何确定类?

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 01 项目分类 1.1、无数据库的项目&#xff1a;应用面向对象的思想和发方法设计&#xff0c;完成各个类的设计过程&#xff0c;确定各个类之间的关系。 1.2、有数据库的项目&#xff1a;项目的框架和思路相对固定&…

【风电功率预测-粉丝福利】向量加权平均算法优化卷积神经网络结合长短记忆网络INFO-CNN-LSTM

如何做 风电功率预测是一项重要的任务&#xff0c;可以帮助优化风电发电效率和电网调度。为了提高风电功率预测的准确性&#xff0c;可以使用卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;结合长短期记忆网络&#xff08;Long Short-Term Memory, L…

管道通信机制详解:无名管道 vs 有名管道

目录 无名管道&#xff08;匿名管道&#xff09; 定义 特点 创建与使用 有名管道&#xff08;FIFO&#xff09; 定义 特点 创建与使用 总结 在多进程通信中&#xff0c;管道是一种非常基本且实用的机制&#xff0c;它允许进程间进行数据传输。根据是否在文件系统中有名称&#…

2024中国振威化工装备展

2024上海国际化工设备展览会 第十六届上海国际化工装备博览会将于2024年11月19-21日在国家会展中心&#xff08;上海&#xff09;举办&#xff0c;预计参展企业1000多家&#xff0c;展览面积7万平方米&#xff0c;观众突破10万人次。展会设置石化装备、化工单元设备、化工环保…

Flutter 中的 ChoiceChip 小部件:全面指南

Flutter 中的 ChoiceChip 小部件&#xff1a;全面指南 在Flutter中&#xff0c;ChoiceChip是一种特殊的组件&#xff0c;用于表示一组可选项中的单个选项。它通常用于实现简单的选择功能&#xff0c;如单选按钮或复选框。本文将详细介绍如何在Flutter应用中使用ChoiceChip。 …