倍增LCA

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

朴素算法求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;若没重合,则两节点同时用倍增法上跳,直到两节点的父亲节点重合,则两点中任意节点的父亲节点即为lca。

模板题 POJ-1330 求两节点的lca

数据结构中的树,在计算机科学中是非常重要的,例如我们来看看下面这棵树:
 

 
在图中我们对每个节点都有编号了。 8号节点是这棵树的根。我们定义,一个子节点向它的根节点的路径上,任意一个节点都称为它的祖先。例如, 4号节点是16号节点的祖先。而10号节点同样也是16号的祖先。事实上,16号的祖先有8,4,10,16共四个。另外8, 4, 6,7都是7号节点的祖先,所以7号和16号的公共祖先是4和8号,而在这两个里面,4号是距离7和16最近的一个,所以我们称7号和16号的最近公共祖先是4号。

再例如,2和3的最近公共祖先是10,再例如6和13的是8。

现在你需要编写一个程序,在一棵树中找出指定两个节点的最近公共祖先
 

Input

第一行输入T表示有T组数据。每组第一行是N表示这棵树有多少个节点,其中 2<=N<=10,000。 节点用正整数1, 2,..., N表示。 接下来的 N -1 行表示这棵树的边,每行两个数,都是节点编号,前一个是后一个的父节点。最后一行是要查询的两个节点,计算出这两个节点的最近公共祖先

Output

对于每组测试输出一行,输出它们的最近公共祖先的编号。

Sample Input

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

Sample Output

4
3

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int N=1e4+5;
int n;
vector<int>G[N];
int f[N][30];//祖先信息,f[j][i],节点j的2^i级祖先,f[j][0]即为节点j的父节点
int dep[N];//节点深度信息
void dfs(int u,int f,int d)
{dep[u]=d;for(int i=0;i<G[u].size();i++){if(G[u][i]!=f)dfs(G[u][i],u,d+1);}
}void init(int n)
{int x=1;while(f[x][0]!=-1)x=f[x][0];//找到根节点dfs(x,-1,0);for(int i=1;i<=20;i++){for(int j=1;j<=n;j++){f[j][i]=f[f[j][i-1]][i-1];//预处理}}
}
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])  //用此方法一直wa 
//		x=f[x][i];
//	}for(int i=0;i<=20;i++)if((dep[x]-dep[y])>>i&1)  //AC 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 main()
{int t;cin>>t;while(t--){cin>>n;memset(f,-1,sizeof(f));memset(dep,0,sizeof(dep));for(int i=0;i<=n;i++)G[i].clear();for(int i=1;i<n;i++){int a,b;cin>>a>>b;f[b][0]=a;G[a].push_back(b);G[b].push_back(a);}init(n);	int x,y;cin>>x>>y;cout<<lca(x,y)<<endl;}return 0;} 

POJ-1986

John是一个农场主,他有许多农场,每个农场里都会养一些牛,并且他的农场有一个特点,就是每两个农场之间,至多存在一条路线。他有时需要将某个农场的牛带到另一个农场去,但是,他的牛都懒散惯了,所以John想为他的牛找到一条最短的路线。先给出John的农场地图,请你为他计算某两个农场间的最短距离。

输入格式

第一行给出两个整数 n,m(n,m\le 40000)n,m(n,m≤40000),表示农场数和道路数。

接下来有 mm 行,每行给出三个整数 a,b,ca,b,c 和一个字符 xx,表示农场 aa 到农场 bb 的有一条直接相连的长度为 cc 的道路,字符 xx 只会是N,S,W,EN,S,W,E,表示north, south, west, east,指从农场a到农场b的道路的方向。注意:所有道路都是双向道路。

接下来一行给出一个整数 k(1\le k\le10000)k(1≤k≤10000),表示询问次数。

接下来有 kk 行,每行给出两个整数 a,ba,b,表示询问农场 aa 到农场 bb 的最短距离。

本题为POJ的题目,请使用G++提交,并且头文件中不能包含unordered_map。

输出格式

对于每个询问,输出农场 aa 到农场 bb 的最短距离。

每行输出末尾不能有多余空格。

输入样例

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4
2 6

输出样例

13
3
36

心得:

输入的N,E,S,W貌似没啥用

这题的不像上面那道题(在输入边时,告诉了两节点中谁是谁的父亲节点),未告诉父亲节点的信息,不妨都把节点1当作根节点,然后dfs中保存节点的父亲节点信息就行了。

这题是lca求树上最短路的典型例题,(树:有n个节点,n-1条边),例如求x,y的最短路,x,y之间的最短路=x到根节点的距离+y到根节点的距离 - 2倍的lca(x,y)到根节点的距离。

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=4e4+5;
struct edge{int to,cost;
};
vector<edge>G[N];
int dep[N],fa[N][30],dis[N];
int t,n,m;
void dfs(int u,int f,int d)
{dep[u]=d;for(int i=0;i<G[u].size();i++){edge e=G[u][i];if(e.to!=f){    //不是父节点,再进行下面操作 dis[e.to]=dis[u]+e.cost;//保存节点到根节点1的距离fa[e.to][0]=u;//保存节点的父亲节点dfs(e.to,u,d+1);}}
}
void init()
{dis[1]=0;
//	fa[1][0]=1;dfs(1,-1,0);for(int i=1;i<=20;i++){for(int j=1;j<=n;j++){fa[j][i]=fa[fa[j][i-1]][i-1];}}
}
int lca(int x,int y)
{if(dep[x]<dep[y])swap(x,y);for(int i=0;i<=20;i++){if((dep[x]-dep[y])>>i&1)x=fa[x][i];}
//	for(int i=20;i>=0;i--)
//	{
//		if(dep[fa[x][i]]>=dep[y])//此方法也能AC
//		x=fa[x][i];
//	 } if(x==y)return x;for(int i=20;i>=0;i--){if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];}return fa[x][0];
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int a,b,c;char cc;scanf("%d%d%d",&a,&b,&c);cin>>cc;edge e;e.cost=c;e.to=b;G[a].push_back(e);e.to=a;G[b].push_back(e);}init();int q;cin>>q;for(int i=1;i<=q;i++){int x,y;scanf("%d%d",&x,&y);//			int l=lca(x,y);cout<<dis[x]+dis[y]-2*dis[lca(x,y)]<<endl;}return 0;} 


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

相关文章

倍增算法

倍增算法 【序言】 我认为吧,所有能够优化复杂度的算法都是神奇的,所有能够化繁琐为形象的文字都是伟大的。一直觉得倍增算法是个很神奇的东西,所以决定写点东西纪念一下它。但是作为一个非常不称职的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…

V4L2简介

http://work-blog.readthedocs.org/en/latest/v4l2%20intro.html 第一章 V4L2简介 1.1、什么是v4l2 V4L2&#xff08;Video4Linux的缩写&#xff09;是Linux下关于视频采集相关设备的驱动框架&#xff0c;为驱动和应用程序提供了一套统一的接口规范。 V4L2支持的设备十分广泛&…

我们一起学linux之V4L2摄像头应用流程

一、概述 Video for Linuxtwo(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频…