首页 > 其他分享 >最近公共祖先(LCA)笔记

最近公共祖先(LCA)笔记

时间:2024-12-23 13:20:17浏览次数:4  
标签:结点 祖先 LCA 笔记 leq int 最近 公共

最近公共祖先(LCA)笔记

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

题目入口

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 \(N-1\) 行每行包含两个正整数 \(x, y\),表示 \(x\) 结点和 \(y\) 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 \(M\) 行每行包含两个正整数 \(a, b\),表示询问 \(a\) 结点和 \(b\) 结点的最近公共祖先。

输出格式

输出包含 \(M\) 行,每行包含一个正整数,依次为每一个询问的结果。

样例 #1

样例输入 #1

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
样例输出 #1
4
4
1
4
4

提示

对于 \(30\%\) 的数据,\(N\leq 10\),\(M\leq 10\)。

对于 \(70\%\) 的数据,\(N\leq 10000\),\(M\leq 10000\)。

对于 \(100\%\) 的数据,\(1 \leq N,M\leq 500000\),\(1 \leq x, y,a ,b \leq N\),不保证 \(a \neq b\)。

样例说明:

该树结构如下:

第一次询问:\(2, 4\) 的最近公共祖先,故为 \(4\)。

第二次询问:\(3, 2\) 的最近公共祖先,故为 \(4\)。

第三次询问:\(3, 5\) 的最近公共祖先,故为 \(1\)。

第四次询问:\(1, 2\) 的最近公共祖先,故为 \(4\)。

第五次询问:\(4, 5\) 的最近公共祖先,故为 \(4\)。

故输出依次为 \(4, 4, 1, 4, 4\)。

2021/10/4 数据更新 @fstqwq:应要求加了两组数据卡掉了暴力跳。


大致思路

就原本是两个点一步一步往上跳直到跳到同一点上。优化的方法是根据两个节点的的深度,如不同,向上调整深度大的节点,使得两个节点在同一层上,如果正好是祖先,结束;否则,将两个节点同时上移,查询最近公共祖先(两个过程均使用倍增加速)。

code

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,m,rt;
vector<int>g[N];
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
int f[N][21];//f[i][j] 结点i往上跳2^j步的祖先结点
//f[i][j]=f[f[i][j-1]][j-1]
int d[N];//d[i] 结点i处于第几层
void dfs(int u,int fa)
{
	f[u][0]=fa;
	for(int i=0;i<(int)g[u].size();i++)
	{
		int v=g[u][i];
		if(v==fa) continue;
		d[v]=d[u]+1;
		dfs(v,u);
	}
}
int get_lca(int x,int y)
{
	//d[x]>d[y]
	if(d[x]<d[y]) swap(x,y);
	int cha=d[x]-d[y];
	for(int i=20;i>=0;i--)
		if(cha>>i&1) x=f[x][i];//x和y处于同一层
	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()
{
	n=read();m=read();rt=read();
	for(int i=1;i<n;i++) 
	{
		int x=read(),y=read();
		g[x].push_back(y);g[y].push_back(x);
	}
	d[rt]=1;
	dfs(rt,rt);
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	while(m--)
	{
		int x=read(),y=read();
		printf("%d\n",get_lca(x,y));
	}
	return 0;
}

标签:结点,祖先,LCA,笔记,leq,int,最近,公共
From: https://www.cnblogs.com/yingxilin/p/18623744

相关文章

  • 并发编程笔记九-异步编程
    一.前言        在Java编程那浩瀚无垠的知识海洋中,隐藏着一片深邃而神秘的领域——并发编程,它宛如一片广袤无边的深海,等待着勇敢的探索者去征服。过去的八节笔记,犹如一盏盏明亮的灯塔,引领着我们从浅滩一步步深入,共同见证了多线程的灵动、锁与AQS的精妙、阻塞队列的稳......
  • Linux驱动开发笔记(七):操作系统MMU介绍,操作系统操作寄存器的原理和Demo
    前言  做过单片机的都知道,写驱动是直接代码设置和读取寄存器来控制外设实现基本的驱动功能,而linux操作系统上是由MMU(内存管理单元)来控制,MMU实现了虚拟地址与芯片物理地址的对应,设置和获取MMU地址就是设置和获取映射的物理地址,从而跟单片机一样实现与物理硬件的驱动连接。 ......
  • 【学习笔记】多重背包优化
    basictips多重背包可以看做\(\texttt{01}\)背包和完全背包的结合。例题:P1776宝物筛选这道题完全就是多重背包板子,多重背包就是在\(\texttt{01}\)背包与完全背包两者间取了个折中,对于每个体积\(V_i\),价值\(W_i\)的物品多了一个限制,每种物品有且仅有\(C_i\)个。朴素......
  • 【差分约束】学习笔记
    BasicTips差分约束,即为存在一个差分约束系统,即类似\(x_i-x_j\leqk\)的\(n\)元一次不等式组,求出一组解使得该组内所有不等式全部成立,即\(x_1=s_1,x_2=s_2\dotsx_n=s_n\),否则判无解。对于满足条件的一个解集\(\{s_1,s_2,s_3,\dots,s_n\}\),集合\(\{s_1+t,s_2......
  • 【AI学习笔记4】四种主流的神经网络 FNN、RNN、CNN、Transformer
     一、人工神经网络的分类最常用的人工神经网络(ArtificialNeuralNetwork,ANN)主要包括以下四种:前馈神经网络(FeedforwardNeuralNetwork,FNN)、循环神经网络(RecurrentNeuralNetwork,RNN)和卷积神经网络(ConvolutionalNeuralNetwork,CNN),还有当前最流行的大模型常用的Transformer神......
  • 蓝桥杯——嵌入式学习笔记
    备战2025蓝桥杯嵌入式,记录一下过程。不定期更新,欢迎提出问题和指导。一、cubemx配置    1.芯片选择        嵌入式主板用的是STM32G431RBT系列,因此选择以下芯片    2.Pinout&Configuration        这里调整System......
  • INA226折腾笔记(一)
    其实也算不上折腾,这个模块很简单就一个简单的I2C模块。随便搞个单片机就能读写,之所以玩这个,主要还是对手上的USB表不满意(换句话说就是达不到我的需求)我手上有几个USB表左边那个高仿版FNB58,右边那个是科维斯的CC表型号为KWS-1902C,平时就是测测手机充电功率之类的,也够用,FNB58还带......
  • 8086汇编(16位汇编)学习笔记01.汇编基础和debug使用
    原文链接:https://bpsend.net/thread-100-1-2.html     为什么学习16位汇编?16位操作指令最多能够操作两个字节,且更能够体现出与硬件的交互。16位下的指令和32位汇编的指令差不多。16位汇编的指令在32位一样使用.要学好汇编必须要了解一点点硬件知识,16汇编是直接操作......
  • 【学习笔记】高位前缀和(SOSDP)
    高位前缀和解决这样的问题:给定\(f_i\),其中\(i\in[0,2^n-1]\),求解\(\sum\limits_{j\ini}f_j\)。考虑一维前缀和:for(inti=1;i<=n;i++){ sum[i]=sum[i-1]+a[i];}二位前缀和:for(inti=1;i<=n;i++){ for(intj=1;j<=n;j++){ sum[i][j]=sum[i][j-1]+a[i][j]; }}for......
  • 三维测量与建模笔记 - 7.2 点云滤波
        逐点计算法向量,需要对每一个点拟合出它的切平面,一般使用邻域点信息来查找切平面。    选取要计算的点和它周围一定范围内的点可以拟合出一个平面,最基本的方法是通过最小二乘法取对这些点到平面的距离进行优化(计算量很大)。可以通过计算协方差矩阵来实现......