倍增求lca

LCA四种写法其一:倍增 文章目录众所周知一、倍增区间倍增二、LCA2.读入数据总结 众所周知 LCA有四种写法 一、倍增 fa[n][logn],fa[i][j]表示i节点的第2^j个父亲。 显然:fa[i][j]=fa[ fa[i][j-1] ][j-1] 即i的第2j个父亲是i的第2(j-1)个父亲的第2(j-1)个父亲。 于是,求i的第k个父亲的复杂度O(k)—>O(logk) 求i的第k个父亲: 1. for(int j=0; j<=log2(k); j++) 2. if((1<<j)&a. 阅读详情

倍增求lca 模板
求最近公共祖先lca(Least Common Ancestors),什么是公共祖先,给定一棵树,若节点z即使节点x的祖先,也是节点y的祖先,则称z是x,y的公共祖先,在x,y的所有公共祖先中,深度最大的节点称x,y的最近公共祖先,记做LCA(x,y)。
举个例子
在这里插入图片描述
LCA(4,5) = 2,LCA(5,6)=1,LCA(2,3)=1;
那么如何求LCA
先给出一张我们接下来要讲的例子:
例子
第一:暴力求法
先暴力dfs出每个点的深度,假设以 171717和 18 为例,既然要求LCA,那么我们就让他们一个一个向上跳,直到相遇为止。第一次相遇即是他们的LCA。 模拟一下就是:
17−>14−>10−>7−>3
18−>16−>12−>8−>5−>3
最终结果就是 3
但是这个算法的时间复杂度却是很高,所以我们采取了第二种重要的方法。
树上倍增法
设pre[x][k]表示x的2k 辈祖先,即从x向根节点走2k步到达的节点,若该节点不存在,则令pre[x][k] = 0,f[x][0]就是x的父节点。
所谓倍增,就是按2的倍数来增大,也就是跳 1,2,4,8,16,32 …… 不过在这我们不是按从小到大跳,而是从大向小跳,即按……32,16,8,4,2,1来跳,如果大的跳不过去,再把它调小。这是因为从小开始跳,可能会出现“悔棋”的现象。拿 5为例,从小向跳,5≠1+2+4,所以我们还要回溯一步,然后才能得出5=1+4;而从大向小跳,直接可以得出5=4+1。这也可以拿二进制为例,5(101),从高位向低位填很简单,如果填了这位之后比原数大了,那我就不填,这个过程是很好操作的。
还是以 17 和 18 为例(此例只演示倍增,并不是倍增LCA算法的真正路径):
17−>3
18−>5−>3
算法时间度为O(logn)。
接下来我们拆分算法步骤:
1:设depth[x]表示x的深度,不妨设depth[x]>=depth[y],否则交换x,y;
2:用上述二进制拆分思想,把x向上调整到与y同一高度。
3:若此时x==y,说明lca(x,y)就是x.
4:若此时x!=y,那么继续保持二进制拆分思想,把x,y同时向上调整,并保持深度一致并且二者不会相会。
/我们在跳的时候不能直接跳到它们的LCA,因为这可能会误判,比如4和8,在跳的时候,我们可能会认为1是它们的LCA,但1只是它们的祖先,它们的LCA其实是3。所以我们要跳到它们LCA的下面一层,比如4和8,我们就跳到4和5,然后输出它们的父节点,这样就不会误判了。/
5:此时x,y必定只差一步就会相会了,它们的父节点pre[x][0]就是我们要求的lca。

完整的求17和18的LCA的路径:
17−>10−>7−>3
18−>16−>8−>5−>3
解释:首先,18要跳到和17深度相同,然后18和17一起向上跳,一直跳到LCA的下一层(17是7,18是5),此时LCA就是它们的父亲

在算法实现的过程中,我们可以加上log2(x)log_2(x)log2​(x)的常数时间的优化。
下面给出各个步骤代码。
预处理:

void dfs(int f,int fa){
	depth[f] =  depth[fa] + 1;
	pre[f][0] = fa;
	for(int i=1;(1<<i) <= depth[f];i++)
		pre[f][i] = pre[pre[f][i-1]][i-1];//这个转移可以说是算法的核心之一
                                //意思是f的2^i祖先等于f的2^(i-1)祖先的2^(i-1)祖先
                                //2^i=2^(i-1)+2^(i-1)
	for(int i=head[f];i;i=nex[i]){
		if(ver[i] != fa)
		dfs(ver[i],f);
	}
}

查询x,y的lca:

int lca(int x,int y){
	if(depth[x] < depth[y]) swap(x,y); ////用数学语言来说就是:不妨设x的深度 >= y的深度
	while(depth[x] > depth[y])
	x = pre[x][lg[depth[x] - depth[y]]-1]; ////先跳到同一深度
	
	if(x==y) return x;//如果x是y的祖先,那他们的LCA肯定就是x了
	
	for(int i=lg[depth[x]]-1;i>=0;i--)  //不断向上跳(lg就是之前说的常数优化)
	if(pre[x][i]!=pre[y][i])  //因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过
	x = pre[x][i],y=pre[y][i];
	
	return pre[x][0]; 
}

给出测试链接:https://www.luogu.org/problem/P3379

给出模板:

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+7,M = 1e6+7;
int n,m,s,cnt;
int pre[N][22],depth[N],lg[N];
int head[M],ver[M],nex[M];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
} 
void read(){
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
void dfs(int f,int fa){
	depth[f] =  depth[fa] + 1;
	pre[f][0] = fa;
	for(int i=1;(1<<i) <= depth[f];i++)
		pre[f][i] = pre[pre[f][i-1]][i-1];//这个转移可以说是算法的核心之一
                                //意思是f的2^i祖先等于f的2^(i-1)祖先的2^(i-1)祖先
                                //2^i=2^(i-1)+2^(i-1)
	for(int i=head[f];i;i=nex[i]){
		if(ver[i] != fa)
		dfs(ver[i],f);
	}
}
int lca(int x,int y){
	if(depth[x] < depth[y]) swap(x,y); ////用数学语言来说就是:不妨设x的深度 >= y的深度
	while(depth[x] > depth[y])
	x = pre[x][lg[depth[x] - depth[y]]-1]; ////先跳到同一深度
	
	if(x==y) return x;//如果x是y的祖先,那他们的LCA肯定就是x了
	
	for(int i=lg[depth[x]]-1;i>=0;i--)  //不断向上跳(lg就是之前说的常数优化)
	if(pre[x][i]!=pre[y][i])  //因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过
	x = pre[x][i],y=pre[y][i];
	
	return pre[x][0]; 
}
void solve(){
	dfs(s,0);
	for(int i=1;i<=n;i++) ////预先算出log_2(i)+1的值,用的时候直接调用就可以了
	lg[i] = lg[i-1]+(1<<lg[i-1]==i);
	
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
}
int main(){
	read();
	solve();
	return 0;
}

资料参考:
https://www.luogu.org/blog/morslin/#
李煜东 算法竞赛进阶指南

倍增求LCA 求LCA的过程: 两个节点平层同时往上跳,直到相遇,相遇的点就是他们的LCA。但是很明显,如果这棵树的深度较大,那么就要跳很久了,所以可以采用倍增优化 设 F[x, k] 表示 x 的 第 2k 个祖先,即 x 节点向根节点走 2k 步到达的节点。f[x, 0] 就是 x 节点第一个祖先(也即父节点)。若该节点不存在(跳到了根节点外),我们则令 f[x, k] = 0 因为从x走2^k步, 可以分为先走2(k-1)步,再走2(k-1)步 得出F[x,k]的递推方程 ∀k∈[1,logn],F[x,k] 阅读详情

相关推荐

倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

目录一、倍增算法二、倍增算法的应用:求LCA(最近公共祖先)附模板题三、倍增算法的应用:RMQ 问题(ST表)附模板题 一、倍增算法 要了解倍增之前,建议先看一下大佬的博客:【白话系列】倍增算法 看完以后相信你已经对倍增有了大致初步的了解,下面给出倍增的定义 (其实没什么用,好长一段话建议不看) 倍增 从字面的上意思看就是成倍的增长 ,这是指我们在进行递推时,如果状态空间很大,通常的线性递推无法满...

繁凡さん的博客 1万+

LCA问题(倍增法)

之前写过了LCA的两个在线算法之一:DFS+ST方法(DFS+ST)。本篇介绍另一种在线算法,用倍增思想来解决LCA问题。在学习倍增之前,先看一种倍增算法的退化版,有助于理解倍增法。 在找a和b的LCA时,先让a和b中深度较大的那一个,向上回溯到与另一个同样深度的位置上。例如下面这个图: 假设要找F和E的LCA,先让F向上回溯到他的父节点D的...

Q_M_X_D_D_的博客 1万+

使用MLP解决OCR问题(OpenCV)(下)

分类模型:        分类模型涉及的一个比较关键的问题就是输出的10维向量是如何与具体的类别挂钩的。实际上:10维向量的每一位都代表一类,在对于训练集的表达中,如果输入数据是0,则10维向量的第一位赋值为1,其余均为0。即0对应[1,0,0,0,0,0,0,0,0,0]。MLP模型训练完成后,就需要对用户输入的数据所属类别进行判定。这时得到的输出数据基本不可能是正好的所属类为1,其他位置为0

qq_18515405的专栏 1906

倍增LCA

朴素算法求LCA,即暴力求解,当树近乎为一条链时,找祖先时一级一级向上跳,时间复杂度接近O(n),所以可以考虑一次跳尽可能大的距离,即倍增算法;一次跳2^i(i=0,1,2....)级。 倍增法 一个节点的四级祖先就是这个节点的二级祖先的二级祖先; 设fa[j][i]为j节点的2^i级祖先;则fa[j][i]=fa[fa[j][i-1]][i-1]; 算法思想:求两节点的lca,先将深度深的结点用倍增法向上跳,直到两节点处于同一深度,此时有可能重合,则两点中任意一个结点即为lca;若没重合,则两节

qq_59144780的博客 4947

别再只玩积木了!用乐高EV3和官方软件,手把手教你做个能巡线的小车(附完整代码)

本文详细介绍了如何利用乐高EV3套件和官方软件制作智能巡线机器人,从硬件组装到软件编程全面覆盖。通过传感器原理讲解、控制算法调试等工程化细节,帮助读者掌握机器人开发的核心理念,特别适合STEAM教育和硬件编程爱好者实践。

weixin_30776273的博客 449

实战避坑:在Ubuntu 22.04上配置NVIDIA GPU Direct RDMA时,必须搞清的P2P DMA与IOMMU那些事

本文深入探讨在Ubuntu 22.04上配置NVIDIA GPU Direct RDMA时P2P DMA与IOMMU的关键问题,解析PCIe架构中的地址转换、P2P DMA与标准DMA的区别,以及IOMMU对性能的影响。通过实战案例和调优建议,帮助用户避免常见配置陷阱,提升AI训练集群的数据传输效率。

weixin_33720078的博客 441

倍增法在线求LCA(详解)

先贴上学习的文章: 洛谷P3379题解1 倍增讲解 在用Tarjan算法求LCA时会出现超时的现象,因为是一层一层的往上跳,于是就有了用倍增法求LCA,是层地往上跳。复杂度:O(nlogn) 1.倍增法思想: 按2的倍数跳,不过是从大到小跳,32,16,8,4,2,1。原因是从小到大跳会出现“悔棋”的现象: 从小到大:5!=1+2+4 从大到小:5=4+1 5<32 (不跳)...

m0_37579232的博客 1978

2026年AI编程工作流实战:Claude Code与Open Code深度对比

AI编程已从代码生成工具升级为IDE级智能工作流核心能力。其本质是将大语言模型深度嵌入开发环境,通过语义理解、上下文感知与行动闭环重构传统编码范式。Claude Code以企业级知识图谱和双轨Code Review为代表,强调合规性、可审计与深度集成;Open Code则依托Skill可组合性、本地优先模型与ocp协议,实现开源驱动的灵活扩展。二者共同推动IDE从‘编辑器’进化为‘数字搭档’——能理解RFC文档、记住PR评论、自动补全带重试的fetch逻辑,并在Promise链发呆时一键重构为async/a

weixin_33739523的博客 462

倍增求 LCA

LCA 指的是最近的公共祖先,倍增法求解 LCA 的步骤如下。 a. 求深度:对于每个节点 dfs 预处理处节点深度; b. 求倍增祖先:计算出每个节点向父元素跳 步所到达的节点(在这里 大于整棵树的最大深度) 备注: a. 设 f [ x , k ] 表示节点 x 向上跳 步能到达的祖先, f [ x , 0 ] 表示 x 的父元素,如对应节点不存在 f [ x , k ] = 0。 b. 将路径长度 分为两半,x 跳

Ryan_hsMax的博客 877

【WSL2 Win10】解决子系统中nividia-smi出现的Failed to initialize NVML GPU access blocked by the operating systeM

环境:WIN10专业版 WSL2 Ubuntu x64架构 尝试在WSL2中装CUDA,装好以后在wsl系统中输入nvidia-smi命令,结果报错。但是在Win10的CMD中正常显示 Failed to initialize NVML: GPU access blocked by the operating system Failed to properly shut down NVML: GPU access blocked by the operating system在网上搜了一下,没太多类似的情况

6745

k8s配置habor作为镜像地址之后,如果harbor中没有镜像,如何设置自动从阿里云官方镜像仓库下载

通过 Harbor 的远端仓库可以集中管理。如果你在上游设置了 image prefix 为 library,那么对外访问 harbor.your-domain.com/library/nginx:latest 就会被 Harbor 去上游拉取 registry.cn-hangzhou.aliyuncs.com/library/nginx:latest 并缓存。- 将集群中的镜像地址改为 Harbor 的域名,比如 harbor.your-domain.com/library/nginx:latest。

极客栈 422

倍增 || ST-RMQ 求LCA

视频讲解戳这里(bj聚聚讲的非常好,一听就懂) 由luogu一道模板题讲起传送门(都9102年,这题还卡输入输出,要吸氧才能过) LCA:求两个点的最近公共祖先 倍增求LCA: 有很多博客图文并茂讲的非常清楚,倍增LCA的主要思想就是,先将u,v(保证u的深度更深)两点调整到同一水平线 情况一:u==v,那就说明最近公共祖先是u,这种情况说明u,v在同一边,祖先自然是深度较浅的那一个...

一只不争气的蒸汽机的博客 345

Python爬虫实战:如何爬取12306列车信息

本文介绍了如何使用Python编写爬虫,抓取12306列车信息。我们讲解了如何分析12306的列车数据接口,如何发送HTTP请求,解析返回的JSON数据,并将数据保存到本地文件中。我们还讨论了如何绕过反爬虫机制,如使用随机User-Agent和设置请求延时。通过这个实战项目,你应该掌握了如何爬取并解析来自公共API的数据,同时提高了你的爬虫技能。这些技能不仅适用于12306列车信息抓取,也可以广泛应用于其他需要抓取JSON数据的网站。

2201_76125261的博客 1922

树上倍增法求 LCA

LCA(Lowest Common Ancestor,最近公共祖先)是树论中一个非常重要的概念,指的是在一棵树上两个节点最近的共同祖先节点。求解 LCA 的算法有很多,其中树上倍增法以其高效的查询效率(预处理 O (n log n),查询 O (log n))成为最常用的方法之一。本文将详细介绍树上倍增法的原理,并分享一份可直接复用的实现模板。

x3468714225的博客 595

华为OD机试2025A卷 - 用户调度问题(Java & Python& JS & C++ & C )

题目要求每个用户依次选择当前所能选择的最小资源消耗策略,并且若有多个策略的资源消耗相同,则选择最后一个策略。每个用户的选择是独立的,只考虑该用户的策略和相邻用户的约束,不考虑整个调度序列的全局资源消耗。每个用户根据自己的当前状态(即自己的三种选择的资源消耗)做出局部最优选择:在当前可选的策略中,选择消耗最小的那个。如果有多个最小值,则选择最后一个(根据题意)。这一选择是局部的最优,即在当下最有利的选择,但并不保证最终的整体系统资源消耗是最小的。

算法大师! 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

倍增求LCA模板

#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=5e5+10,maxk=1e4+10,lenOfNode=52274,INF=0x3f3f3f3f; int lg[maxn],n,m,d[maxn],f[maxn][21],dep[maxn],cnt; int nxt...

tgyy_cool的博客 202

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

V4yneの求lca的倍增模板

代码: const int maxn=1e5+50; vector<int> edge[maxn]; int rot;int dep[maxn]; int rec[maxn][30]; void dfs(int x,int fa) { rec[x][0]=fa; dep[x]=dep[fa]+1; for(int i=0;i<edge[x].size();i++) { ...

V4yne.的博客 385

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

树:倍增求LCA(最近公共祖先)

嘿,LCA的题目可是我最喜欢的题目了,今天我就来讲讲如何求LCA? 对于如何LCA,我只会几种,不过比较实用。我重点去讲倍增求LCA。 倍增求LCA 倍增?什么是倍增? 字面上的意思便是成倍增长,其实也就是ST表(不懂的上网搜一搜 ) 言归正传,那我们该怎么用倍增求LCA呢? 首先,我们设 fi,jf_{i,j}fi,j​ 表示从 iii 这个点向上跳 2j2^j2j 步所到达的点。 然后我们可以一遍dfs,处理出所有的 fi,0f_{i,0}fi,0​ 即每个点的父亲。 接着我们考虑如何转移: 首先我们先

LeeCongWei的博客 304

祖孙询问(倍增法求LCA)(全网最详细)(带图解)

倍增法求LCA的核心思路和步骤通过图片和实例理解起来并不复杂,代码写起来更是很顺畅。如果仍有不理解的地方,欢迎留言或者私信。

2301_77160836的博客 1503

倍增法求LCA模版

用倍增法求LCA,板子好打,不容易错。 #include #include #include #include #include using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define

fouzhe的博客 784

树上倍增求LCA

#include #include #include #include #include #include #include #include #include #include #define MAXN 500007 using namespace std; int N; int M; int S; int cnt; int f[MAXN]; int lv[MAXN]; int anc[MA

Angel_wing_SKY的博客 1056

倍增法求Lca——求大佬指点

良心倍增法求lca大佬写的:https://blog.youkuaiyun.com/wjh2622075127/article/details/81060586 首先说Lca,中文最近公共祖先,见名思意很容易知道是求树上两个点最近的公共祖先 第一种方法:两个点同时向上走,并把经过的点标记,这样当有一个点走到被标记的点的时候,这个点就是要找的点。 第二种方法:很明显第一种方法是最朴素的,下面来说优于第一种...

wearegamer的博客 228
上一篇: 1496:【例 3】架设电话线
下一篇: 1553:【例 2】暗的连锁 树上差分
A_Pathfinder
博客等级 码龄8年 29粉丝 90原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值