倍增求LCA

article/2025/9/29 15:28:23

一:定义

LCA指的是最近公共祖先。具体地,给定一棵有根数,若结点z既是结点x的祖先,也是结点y的祖先,则称z是x,y的公共祖先。在x,y的公共祖先中,深度最大的那个节点成为x,y的最近公共祖先,记为LCA(x,y)。

我们举个例子,如图4-4-1所示LCA(4,5)=2,LCA(5,6)=1,LCA(2,3)=1;

请添加图片描述

二:如何求LCA

我么考虑“暴力”要怎么实现找两点的LCA。
请添加图片描述
e.g. LCA(7,5)=2;

先DFS一遍找出每个点的DEP(深度)。然后先从深度大的7往上跳,跳到和5深度相同的点4,发现还是没有到同一个点,那么4、5继续往上跳,直到跳到2位置,发现点一样了,那么2就是它们的LCA了。

三:如何优化这个方法

我们考虑这个方法慢在哪里,当然是对于每个点,一次往上跳一步,导致了效率低,那么如何优化呢?只要一次能向上跳多步,效率自然就高了。

树上倍增法

树上倍增法是一个很重要的算法。设f[x,k]表示x的2^k辈祖先,即从x向根结点走
2^k步到达的结点,特别地,若该结点不存在,则令f[x,k]=0。f[x,0]就是x的父结点。 因为x向根结点走2^k <=> 向根走2^(k-1)步, 再走2^(k-1)步。
所以对于k∈[1,logn],有f[x][k]=f[f[x][k-1]][k-1]。

这类似于一个动态规划的过程,“阶段”就是结点的深度,因此,我们可以对树进行遍历,由此得到f[x.0],再计算f数组所有值。
以上部分是预处理,时间复杂度为O(nlogn)。之后可以多次对不同的x,y计算LCA,每次询问的时间复杂度为O(logn)。

基于f数组计算LCA(x,y)分为以下几步:
①设dep[x]表示x的深度。不妨设dep[x]>=dep[y](否则,可交换x,y)。
②用二进制拆分思想,把x向上调整到与y同一深度。
具体来说,就是依次尝试从x向上走k=2^(logn)...2^12^0步,若到达的结点比y深,则令x=f[x,k]。
③若此时x=y,说明已经找到了LCA,LCA就等于y。
④若此时x!=y,那么x,y继续往上跳,用二进制拆分思想,把x,y同时向上调整,并保持深度一致且二者不相会。
具体来说,就是依次尝试把x,y同时向上走k=2^(logn)2^12^0步,若f[x,k]!=f[y,k].(即仍未相会),则令x=f[x,k],y=f[y,k]。
⑤此时x,y必定只差一步就相会了,它们的父结点f[x,0]就是LCA。

【代码实现】
预处理:

void Deal_first(int u,int father){Dep[u] = Dep[father] + 1;for(int i=0;i<=19;i++)f[u][i+1] = f[f[u][i]][i];for(int e=first[u];e;e=next[e]){int v=go[e];if(v == father)continue;f[v][0] = u;//v向上跳2^0 = 1就是uDeal_first(v,u);}
}

查询x,y的LCA:

int LCA(int x,int y){if(Dep[x]<Dep[y])swap(x,y);//让x深度较大
//我们用“暴力”的思想:先将x、y跳到一个深度,然后一起往上跳for(int i=20;i>=0;i--){//一定要倒着forif(Dep[f[x][i]]>=Dep[y])x=f[x][c][i];//先跳到同一层if(x==y) return x;}for(int i=20;i>=0;i--){//此时x,y已跳到同一层if(f[x][i]!=f[y][i])//如果f[x][i]和f[y][i]不同才跳{x=f[x][i];y=f[y][i];}}return f[x][0];//x,y是深度最浅且不同的点,即LCA的子结点
}

例题:

点的距离 LibreOJ - 10130

题目描述
给定一棵 n 个结点的树,Q个询问,每次询问点 x 到点 y 两点之间的距离。

输入格式
第一行一个正整数 n,表示这棵树有 n 个节点;
接下来 n-1 行,每行两个整数 x,y 表示 x,y 之间有一条连边;
然后一个整数 Q,表示有 Q 个询问;
接下来 Q 行每行两个整数 x,y 表示询问 x 到 y 的距离。

输出格式
输出 Q 行,每行表示每个询问的答案。

样例
Input
6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
Output
3
4
数据范围与提示
对于全部数据,1≤n≤10^5,1≤x,y≤n。

分析:我们需要构建一棵树,然后距离就是x的深度+y的深度-LCA(x,y)的深度。
AC代码:

#include <bits/stdc++.h>
using namespace std;
const int ONE=100010;
int n,Q,x,y;
int nexx[ONE*2],first[ONE*2],go[ONE*2],tot;
int Dep[ONE];
int f[ONE][22];
void Add(int u,int v){nexx[++tot]=first[u];first[u]=tot;go[tot]=v;
}
void Deal_first(int u,int father){Dep[u]=Dep[father]+1;for(int i=0;i<=19;i++){f[u][i+1]=f[f[u][i]][i];}for(int e=first[u];e;e=nexx[e]){int v=go[e];if(v==father)continue;f[v][0]=u;Deal_first(v,u);	}
}
int LCA(int x,int y){if(Dep[x]<Dep[y])swap(x,y);for(int i=20;i>=0;i--){if(Dep[f[x][i]]>=Dep[y])x=f[x][i];if(x==y)return x;}for(int i=20;i>=0;i--){if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}}return f[x][0];
}
int dist(int x,int y){return Dep[x]+Dep[y]-2*Dep[LCA(x,y)];
}
int main(){tot=0;scanf("%d",&n);for(int i=1;i<n;i++){scanf("%d%d",&x,&y);Add(x,y);Add(y,x);}Deal_first(1,0);scanf("%d",&Q);while(Q--){scanf("%d%d",&x,&y);printf("%d\n",dist(x,y));}return 0;
}

详细请看一本通


http://chatgpt.dhexx.cn/article/IPcvHW7P.shtml

相关文章

倍增数组基础

应用&#xff1a; 给定一个数列a, 基本的倍增数组可以用O(1)的时间复杂度计算出区间[i,j]的最值。 具体操作&#xff1a; 维护倍增数组 1、状态 dp[j][i]表示区间从i到 i(2^j)-1,即以i开始长度为2^j 这个区间范围的最值。&#xff08;下面以最小值为例&#xff09; 2、状态转移…

倍增LCA

朴素算法求LCA&#xff0c;即暴力求解&#xff0c;当树近乎为一条链时&#xff0c;找祖先时一级一级向上跳&#xff0c;时间复杂度接近O(n),所以可以考虑一次跳尽可能大的距离&#xff0c;即倍增算法&#xff1b;一次跳2^i&#xff08;i0,1,2....&#xff09;级。 倍增法 一个节…

倍增算法

倍增算法 【序言】 我认为吧,所有能够优化复杂度的算法都是神奇的,所有能够化繁琐为形象的文字都是伟大的。一直觉得倍增算法是个很神奇的东西,所以决定写点东西纪念一下它。但是作为一个非常不称职的OIER,我非常讨厌在看别人的算法解析时整版的i,j,k等我看到鼠标…

浅谈倍增及应用

浅谈倍增 倍增思想 原理&#xff1a; 倍增&#xff0c;即成倍增长。在处理一系列序列数据时&#xff0c;倍增可通过指数级的增长快速覆盖所有数据。 倍增算法采用2倍增长的方式&#xff0c;其核心思想是建立以下数组T a[i][j] &#xff0c;表示自i向后共 2 j 2^j 2j个数据的…

倍增(ST表与LCA)

倍增&#xff08;ST表与LCA&#xff09; 什么是倍增&#xff1f; 倍增&#xff0c;就是成倍增长。指我们进行递推时&#xff0c;如果状态空间很大&#xff0c;通常线性递推无法满足时空复杂度要求&#xff0c;那么可以通过成倍增长的方式&#xff0c;只递推状态空间中 2 的整…

V4L2-框架

1.概述 V4L2 是专门为linux 设备设计的一套视频框架&#xff0c;其主体框架在linux内核&#xff0c;可以理解为是整个linux系统上面的视频源捕获驱动框架。 相机驱动层位于HAL Moudle 与硬件层之间&#xff0c;借助linux 内核驱动框架&#xff0c;以文件节点的方式暴露接口给用…

V4L2学习笔记

首先附上&#xff0c;Linux内核网站&#xff1a; 入口 v4l2非常棒的一篇文章 入口 内核代码查看&#xff1a; 入口 一个博客文档&#xff1a; 入口 https://lwn.net/Articles/204545/ 别人整理 关于linux内核头函数&#xff1a; /usr/src/xxx 系列文 在其内部有media文件…

V4L2框架学习

V4L2框架 0 查看videodev2 0.1 头文件/usr/include/linux/videodev2.h 0.2 使用ioctl函数 使用ioctl函数 // 添加头文件 #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h>int ioctl (int __fd, unsigned long int __request, ...…

V4L2介绍

V4L2介绍 V4L2介绍主要功能框架v4L2编程 V4L2介绍 V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中&#xff0c;视频设备是设备文件&#xff0c;可以像访问普通文件一样对其进行读写&#xff0c;摄像头在/dev/video*下&#xff0c;如果只有一个视频…

linux V4L2子系统——v4l2架构(7)之V4L2应用编程

linux V4L2子系统——v4l2架构&#xff08;7&#xff09;之V4L2应用编程 备注&#xff1a;   1. Kernel版本&#xff1a;5.4   2. 使用工具&#xff1a;Source Insight 4.0   3. 参考博客&#xff1a; &#xff08;1&#xff09;Linux V4L2子系统-应用层访问video设备&a…

[2022.8.15]v4l2-ctl基本使用方法

v4l2-ctl使用帮助可以参考&#xff1a;https://www.mankier.com/1/v4l2-ctl 1 v4l2-ctl --list-devices 列出所有设备 USB 2.0 Camera: USB Camera (usb-0000:00:14.0-9):/dev/video0/dev/video1 一个USB camera对应两个设备&#xff1a;一个是图像/视频采集&#xff0c;一…

V4L2应用层分析

V4L2应用层分析 一、总述二、例子 一、总述 V4L2&#xff0c;即Video for Linux 2&#xff0c;是第二代为Linux打造的音频、视频驱动。相比第一代V4L&#xff0c;V4L2支持更多的设备&#xff0c;同时更加稳定。现今的视频设备&#xff0c;如USB摄像头&#xff0c;基本都支持V4…

v4l2数据结构分析

v4l2数据结构分析 文章目录 v4l2数据结构分析Video4Linux2设备v4l2_device媒体设备media_deviceVideo4Linux2子设备v4l2_subdevVideo4Linux2子设备的操作集v4l2_subdev_opsVideo4Linux2子设备的内部操作集v4l2_subdev_internal_opsVideo4Linux2控制处理器v4l2_ctrl_handlerVide…

摄像头V4L2编程应用开发

1.V4L2简介 Video for Linux two(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux系统操作系统下一套用于采集图片、视频、和音视频数据的通用API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。 在linu…

V4L2框架

前言 在分析v4l2之前最好具有的知识&#xff1a; 1.字符设备,因为v4l2是被枚举为字符设备。 2.内存分配和映射,比如相关数据结构的分配和buffer。 3.DMA&#xff0c;因为v4l2的数据传输用到了DMA。 4.I2C&#xff0c;因为很多传感器都是用的i2c接口。 5.文件系统的基本操作&am…

V4L2系列 之 V4L2驱动框架

目录 前言一、V4L2驱动框架概览1、应用层 -》中间层-》驱动层2、主要代码文件(Linux 4.19版本内核) 二、怎么写V4L2驱动1、如何写一个设备的驱动&#xff1f;2、Video设备主要结构体3、怎么写V4L2驱动 三、V4L2的调试工具1、v4l2-ctl2、dev_debug3、v4l2-compliance 前言 本篇文…

V4L2框架概述

原文链接 本文开启 linux 内核 V4L2 框架部分的学习之旅&#xff0c;本文仅先对 V4L2 的框架做一个综述性的概括介绍&#xff0c;然后接下来的文章中会对 V4L2 框架的各个子模块进行一个全面的介绍&#xff0c;包括每一部分的实现原理&#xff0c;如何使用&#xff0c;用在什么…

V4l2框架分析

Table of Contents 1.V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 1.2.1 video_device 1.2.2 v4l2_subdev 1.2.3 videobuf2 2. video_device结构体 2.1 图像处理模块 2.2 video_device处理流程 2.2.1 video_device 结构体成员介绍: 3. video_buf2 3.1 …

V4L2

V4L2(video 4 linux 2) V4L2有一段历史了。大约在1998的秋天&#xff0c;它的光芒第一次出现在Bill Dirks 的眼中。经过长足的发展&#xff0c;它于2002年11 月&#xff0c;发布2.5.46 时&#xff0c;融入了内核主干之中。然而直到今天&#xff0c;仍有一部分内核驱不支持新的A…