树链剖分求LCA[模板] 超详细解释 + 代码

树链剖分求LCA(最近公共祖先) LCA(Lowest Common Ancestor 最近公共祖先)定义如下:在一棵树中两个节点的LCA为这两个节点所有的公共祖先中深度最大的节点。 如图,节点11与节点6的LCA为节点4,节点12与节点1的LCA为8,节点11与节点10的LCA为10。 现在我们来了解一下LCA我认为最好的算法:树链剖分。 我们来看一个对话: A:我考你一个问题:给你一个100000节点的树,问你两个点 阅读详情

 

 

(题外话)

虽然LCA最快不是树链剖分 但由于想学 于是刻苦钻♂研了下

由于网上那些都是零零碎碎(这不是我的超链接)的 自己通过东拼西凑(这也不是)加理解终于懂了

然后以自己的理解再复述一遍~可谓是倾囊以授啦~

(正篇)

有关树链剖分的概念戳进这一篇~

原理具体解释于程序中~

模板在这里呐 戳进去即可

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX=500005;
struct Edge //邻接表
{
	int to,next;
}edge[MAX << 1];


int fa[MAX],     top[MAX],   deep[MAX], size[MAX],      son[MAX];
  //该点的父亲 点所在链顶点   点深度值  点子树节点个数 点的重儿子 (分别对应)
int first[MAX],tot;
void add(int i,int j)
{ //邻接表存无向图~
	edge[++tot].to = j;
	edge[tot].next = first[i];
	first[i] = tot;
}


void dfs1(int p) //p 当前节点
{ //第一次dfs,用以求出所有节点的父亲节点 & 深度 & 子节点个数 & 重节点
	size[p] = 1; //p子节点数量初始化 把自己算进去 因为到最后一层的时候用来返回 才能让其父亲得到准确的子节点个数
	deep[p] = deep[fa[p]]+1; //定义p点深度(其父亲+1)
	  for (int a = first[p]; a; a = edge[a].next)
	  { //遍历与p点相连的所有节点
	  	int b = edge[a].to; //p通过与其相连的某一条边所到的点(懒得打于是用一个变量代替)
	  	  if (b == fa[p]) continue; //如果不小心连到了p的父亲节点 不处理 继续下一个点
	  	fa[b] = p; //在要dfs前确定下个节点的父亲节点 如果放外面内存要翻上几番
		dfs1(b);
		size[p]+=size[b]; //统计当前节点的子节点个数
	  	  if (size[b] > size[son[p]]/* || !son[p](可省略 因为前一条已经包括了)*/) son[p] = b;
	  } //(上一行)如果 p的子节点b的子节点  大于  p的原来的重儿子 || 没有设置过重节点(可省略 见下行解释)
}           //因为没有设置时son[p]为0 size[0]初始定义为0 size[b]由于深搜出来肯定大于0 故


void dfs2(int p,int father) //p 当前节点 father 当前节点所在链的起点
{ //第二次dfs求出每个节点所在链的起点(见后面)
	top[p] = father; //将当前节点的链起点记作father
	  if (!son[p]) return; //如果p是当前重链的链尾了则跳出去 接到上一层循环里
	dfs2(son[p],father); //将同一重链上的点的起点father记好
	  for (int a = first[p]; a; a = edge[a].next)
	  { //遍历与p点相连的所有节点
	  	int b = edge[a].to; //p通过与其相连的某一条边所到的点(懒得打于是也用同一个变量代替)
	  	//(下行)如果b不是p的重子节点 & 不是p的父亲节点 则把其顶点设为自己 递归新的重链
	  	  if (b != son[p] && b != fa[p]) dfs2(b,b);
	  }    //(上行补充)新的重链就是在找到轻链以后从它的顶点继续找下去 一定全都能搜到
}


int main()
{
	int n,m,g,i,j;
	scanf("%d%d%d",&n,&m,&g);
	  for (int a = 1; a < n; a++)
	  { //此处输入连接两节点的边 邻接表存无向图 此处不用邻接多重表
	  	scanf("%d%d",&i,&j);
	  	add(i,j);
		add(j,i); //重点
	  }
	dfs1(g);    //第一次深搜
	dfs2(g,g); //第二次深搜
	  for (int a = 1; a <= m; a++)
	  { //此处查询 i 和 j 的 最近公共祖先
	  	scanf("%d%d",&i,&j);
	  	  while (top[i] != top[j]) //如果 i 和 j 各自链的起点不同 则
	  	  { //判断 i 和 j 各自链的起点深度 深度不同时先修改深的
	  	    if (deep[top[i]] < deep[top[j]]) j = fa[top[j]];
	  	    else i = fa[top[i]]; //把要修改的节点的顶点接到该点父亲节点的顶点上(可能意思不太对)
	  	  } //一次只能修改一个 不然两个一起修改两点可能擦肩而过
	  	  if (deep[i] > deep[j]) printf("%d\n",j);
	  	  else printf("%d\n",i);
	  } //两点此时都在同一链上 此时因为是找最近公共祖先 就把深度较小的点输出
	return 0; //(上行解释) 因为深度较大的点还要向上 才能和深度较小的点相遇
}

 

大概就是这样子啦 OvO

 

过模板题用了1344ms 还是比较慢的=-=

然后乱加优化

#define r register
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX=1 << 19;
struct Edge
{
	int to,next;
}edge[MAX << 1];
int fa[MAX],top[MAX],deep[MAX],size[MAX],son[MAX],first[MAX],tot;
inline void read(int &x)
{
    char q = getchar();x = 0;
    	while (q < '0' || q > '9') q = getchar();
    	while (q <= '9' && q >= '0')
	x = (x << 1) + (x << 3) + q - (3 << 4), q = getchar();
}
void writeln(int x)  
{
    tot = 0; char q[8];
    	while (x) q[++tot] = (x%10) + (3 << 4), x /= 10;
    	while (tot) putchar(q[tot--]);
    putchar('\n'); 
}
void add(r int x,r int y)
{
    edge[++tot].to = y;
    edge[tot].next = first[x];
    first[x] = tot;
}
void dfs1(int p)
{
    ++size[p];deep[p] = deep[fa[p]]+1;
    	for (r int a = first[p]; a; a = edge[a].next)
    	{
      		r int b = edge[a].to;
      		if (b == fa[p]) continue;
      		fa[b] = p;dfs1(b);size[p]+=size[b];
      		if (size[b] > size[son[p]]) son[p] = b;
    	}
}
void dfs2(int p,int father)
{
    top[p] = father;
    	if (son[p]) dfs2(son[p],father);
    	for (r int a = first[p]; a; a = edge[a].next)
    	{
    		r int b = edge[a].to;
    		if (b != son[p] && b != fa[p]) dfs2(b,b);
    	}
}
int main()
{
    r int i,j,m;
    int n,g;
    read(n);read(m);read(g);
    	for (r int a = 1; a < n; a++)
    	{
    		read(i);read(j);
    		add(i,j);add(j,i);
    	}
    dfs1(g);
    dfs2(g,g);
      while (m--)
      {
      	read(i);read(j);
      		while (top[i] != top[j])
				if (deep[top[i]] < deep[top[j]]) j = fa[top[j]];
      			else i = fa[top[i]];
      		if (deep[i] > deep[j]) writeln(j);
      		else writeln(i);
      }
    return 0;
}

1140ms =-=

各种LCA的模板 好久没有写博客了,联赛前一天来写一点关于LCA的各种模板 极力推荐第四种方法1.Sparce_Table 算法把树按照DFS序重新编号并求出DFS序列后,利用序列中两点编号之间的最小值即为LCA的性质,通过RMQ求解。 代码如下:/* ID: Sunshine_cfbsl LANG: C++ PROG: LCA */ #include<cstdio> #include<cmath> #incl 阅读详情

相关推荐

【数据结构】树链剖分 (图文代码详解)

树链剖分算法图文详解

qq_49688477的博客 2229

树链剖分 求 最近公共祖先(LCA)

用树链刨分求最近公共祖先的笔记

qq_37746591的博客 1720

对LCA、树上倍增、树链剖分(重链剖分&长链剖分)和LCT(Link-Cut Tree)的学习

一篇从浅到深探究一些树上算法的博客

路人黑的纸巾 1万+

树链剖分原理和 LCA 问题

算法和数据结构

实践求真知 1040

最近公共祖先算法详解LCA(附倍增、Tarjan、树链剖分方法及模板)

最近公共祖先(LCA) 模板题洛谷P3379 定义 两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。 倍增算法 考虑了二进制的思想,预处理每个结点uuu的2i2^i2i层父亲结点的下标,(其中i=0,1,2...,n. 2n≤cntui=0,1,2...,n.\ 2^n \leq cnt_ui=0,1,2...,n. 2n≤cntu​),遍历处理即可,复杂度O(n)O(n)O(n)。然后 首先对于查询的两个结点,将两者转移至同一深度,方式为把深度更深的结点按二进制

hzerotole的博客 567

ubuntu安装eigen3.3.7

查看已安装版本: gedit /usr/include/eigen3/Eigen/src/Core/util/Macros.h 或 gedit /usr/local/include/eigen3/Eigen/src/Core/util/Macros.h 卸载先前版本: sudo updatedb locate eigen3 得到eigen库的位置 卸载: sudo rm -rf /usr/include/eigen3 /usr/lib/cmake/eigen3 ...

reason的博客 1万+

树链剖分 入门(求LCA)

1. 引言   最近学了树链剖分基本思想,然后自己实现了遍代码,过了树链剖分求LCA,本文对树链剖分基本过程进行阐述,提出自己的看法,欢迎交流。 2.基本思想   顾名思义,树链剖分就是将树剖分成一条一条的链,之后快速的操作链。   树链剖分的复杂度为,预处理两边dfs,O(n)复杂度;之后查询或者修改操作每一条链为O(log(n)),因为剖分出来的链一共有log(n)条,当然如果在链上再套个线段树,那每次操作就是O(log2(n))。之后,下文只利用树链剖分求树上两点的最近公共最先,不涉及线段树,了解了基

qq_41418281的博客 4361

百度无人车ApolloAuto使用入门

1.安装原版ubuntu 14.04 http://www.ubuntu.org.cn/download/alternative-downloads 2.安装对应ubuntu 14.04的indigo版ROS http://wiki.ros.org/indigo/Installation/Ubuntu 3.安装Apollo Kernel https://github.co

ss910的博客 3万+

树链剖分(重链剖分)

本文主要针对于还没有学习过这个算法的新手同学

m0_73666576的博客 3958

【异常】SpringCloud Gateway报错503 SERVICE_UNAVAILABLE “Unable to find instance for xxx-gen“

网关服务报错,如下。

奶爸的编程之路,也就一周冷个三天 4223

华为OD机试真题 新系统 2026-05-13 Python&JS语言 实现【查找能被整除的最大整数】

给定一个字符串和一个正整数,字符串由大小写字母和数字组成,要求从字符串中找出最大且能被给定正整数整除的数。

MISAYAONE的博客 223

专题·树链剖分【including 洛谷·【模板】树链剖分

初见安~~~终于学会了树剖~~~ 【兴奋】当初机房的大佬在学树剖的时候我反复强调过:“学树剖没有前途的!!!” 恩。真香。 一、重链与重儿子 所谓树剖——树链剖分,就是赋予一个链的概念来优化一些或者说是应对一些操作的,所以相应的会有一些专用的概念定义。 一个节点的重儿子,为其更大的一颗子树的根节点。从这个点连向重儿子的边我们称为重边。 这里我们定义子树的大小是取决于节点的数量,而...

樱狸✨愿携清风归来处 7231

【2024遥感应用组一等奖】基于风云四号卫星的小时级大气气溶胶和颗粒物监测

最后,比较AGRI观测反射率和查找表计算的反射率反演得到的1小时分辨率AOD数据。然后,使用广泛应用的MODIS DT和DB产品进行对比,图 5显示了它们逐像元尺度的对比,AGRI LaGA算法可以捕捉亚洲地区AOD高低值空间分布格局,比如南亚次大陆、中南半岛、塔克拉玛干沙漠和华北平原是AOD高值区域(平均AOD高于0.3),中国西北、中国南方、中国东北和韩国地区为AOD中值地区(平均AOD在0.2到0.3之间),北纬45度以上以及日本地区为AOD低值区域(平均AOD小于0.2),与全球平均AOD值接近。

JData_Engineer的博客 372

洛谷 - P3379 【模板】最近公共祖先(LCA)(RMQ求LCA/Tarjan求LCA)

题目链接:点击查看 题目大意:给出一棵 n 个点组成的有根树,再给出 m 次询问,每次询问需要回答点 x 和点 y 的 lca 题目分析:今天新学了两种蛮有意思的求 LCA 的方法,总结一下四种方法各有利弊吧,实现复杂度参考我自己,如果有代码大佬能闭着眼随便写的话,忽略即可 Orz 树上倍增求LCA: 时间复杂度: 预处理:O( nlogn ) 查询:O( logn ) 空间复杂度:O( nlogn ) 实现复杂度:最简单 树链剖分求LCA: 时间复杂度: 预处理:O( n )

Falcon的博客 565

STM32 HAL SDADC DMA

由于项目需要使用STM32F373单片机的SDADC功能对电位计进行检测,网上资料比较少,踩了很多坑,下面进行总结。

weixin_44597885的博客 1194

安全与加密常识(10)linux系统证书管理

1. 将自定义CA证书(如my-ca.crt)放到源目录# 2. 编辑配置文件,启用该证书(末尾添加一行 my-ca.crt)# 3. 执行命令同步(核心工作环节)# 4. 验证结果:检查是否生成哈希链接和合并到证书束ls /etc/ssl/certs/my-ca.pem # 证书文件ls /etc/ssl/certs/$(openssl x509 -hash -noout -in /usr/share/ca-certificates/my-ca.crt).0 # 哈希链接。

二进制君 282

LCA(树链剖分)

题目 emmm求树上两点的lca luogu P3379 【模板】最近公共祖先(LCA) 题解 树链剖分的讲解 树剖的思路: 对于在同一条链上的两个点,深度小的即为lca 对于不在同一条链上的两个点,可以把它们跳到同一深度,同理即可 codecodecode #include <map> #include <set> #include <list>...

MILLOPE的博客 394

2026年大数据开发工程师常见面试选择题

大数据面试高频考点解析 摘要:本文汇总了30道大数据开发面试高频题目,涵盖Hadoop生态、Spark、Flink、Kafka等核心组件。重点包括:1)HDFS架构与特性(5V特征、副本机制);2)MapReduce/Spark原理(Shuffle过程、RDD抽象);3)实时计算(Kafka消费组、Flink Watermark);4)数据仓库设计(分层模型、性能优化)。特别强调数据倾斜、小文件问题等实战场景的解决方案,以及从日志、资源等多维度排查任务异常的方法。掌握这些基础原理和典型问题应对策略,是应对大

不务正业的猿 449

树链剖分 --算法竞赛专题解析(30)

树链剖分求LCA、树链剖分的典型应用

罗勇军的博客 2930

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

树链剖分学习&总结

树链剖分性质&板子&一些板子题

I_have_a_world的博客 1095

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

图论学习:树上问题(LCA,树链剖分)

图论:树上问题(LCA,树链剖分) LCA(Least Common Ancestors):最近公共祖先 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u和v的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先 ——百度百科 求LCA的常用算法: 1.离线算法:LCA(tarjan) 2.在线算法:倍增LCA和ST表 1. LCA(Tarjan):...

qq_43597227的博客 796

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

dfs序+树链剖分,超详细讲解+原理分析+模板(看不懂来打我)

dfs序+树链剖分,超详细讲解+原理分析+模板(看不懂来打我dfs序+树链剖分一. 树链剖分能解决什么问题?二.树链剖分前置知识1.dfs序2. 时间戳3.dfs序和时间戳有什么用处线段树的区间修改和查询,对链进行操作三.树链剖分模板四.视频资源讲解 dfs序+树链剖分 一. 树链剖分能解决什么问题? 二.树链剖分前置知识 1.dfs序 2. 时间戳 按照dfs第一次访问的顺序,给每一个节点标记上时间戳 3.dfs序和时间戳有什么用处 开始剖分,跑第二次dfs的作用,也即dfs1和dfs2

TheWayForDream 1653

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

摘要: 本文介绍了求解有根树中最近公共祖先(LCA)问题的多种方法,重点讲解了倍增算法的实现。文章首先描述了同步前进法的基本思路,并指出其O(n)时间复杂度在处理大规模数据时会超时。随后详细阐述了基于倍增思想的优化算法,通过预处理fa数组(记录各节点向上走2^j步到达的节点),将时间复杂度降至O(logn)。文中给出了完整的算法实现步骤,包括初始化、深度优先遍历预处理、以及利用lg数组优化查询过程的具体代码。此外还简要提及了其他解法如Tarjan、DFS序和树链剖分等方法,为读者提供了多种解决LCA问题的思

君义NOIP的博客 1079

LCA(使用树链剖分)

最近刚好学了树链剖分,来一道LCA的模板题练练手:P3379 最近公共祖先(LCA) 题意:给一棵多叉树,m次询问,求两个节点之间的LCA。 思路:树链剖分。 #include <bits/stdc++.h> #include<cstring> #define L(x) (x<<1) #define R(x) (x<<1|1) #define...

qq_41365703的博客 235

树上LCA和树链剖分(未完待续)

好难啊!!!

2403_87718362的博客 835

【树链剖分】模板+树剖lca

树链剖分将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。本文讨论常见的重链剖分各定义来自OI Wiki 树链剖分重子节点子节点中子树最大的子结点轻子节点表示剩余的所有子结点重边从这个结点到重子节点的边轻边到其他轻子节点的边若干条首尾衔接的重边构成重链[fa(x)] 表示节点 [x] 在树上的父亲。[dep(x)] 表示节点 [x] 在树上的深度。[siz(x)] 表示节点 [x] 的子树的节点个数。[son(x)] 表示节点 [x] 的 重儿子。

Mintind的博客 425
下一篇: 树链剖分+线段树维护 的基础操作 较详细原理 + 普(cai)通(ji)模板
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值