首页 > 其他分享 >学习笔记:LCA,最近公共祖先

学习笔记:LCA,最近公共祖先

时间:2024-09-09 22:36:30浏览次数:1  
标签:dep cnt fa 祖先 结点 笔记 int LCA

定义

最近公共祖先(Lowest Common Ancestor),简称LCA,是算法竞赛中常用的、用以查找树上两个结点中,距离根结点最远的结点的算法。

实现

朴素算法

过程

每次找深度比较大的那个点,让它向上跳。显然在树上,这两个点最后一定会相遇,相遇的位置就是想要求的 LCA。 或者先向上调整深度较大的点,令他们深度相同,然后再共同向上跳转,最后也一定会相遇。

复杂度

由于朴素算法在预处理时要dfs整棵树,因此预处理时间复杂度为\(O(N)\)。
单次查询时间复杂度同样为\(O(N)\),在大部分题目的数据范围下都无法通过,所以代码就不贴了。

倍增实现LCA

这里往下才是本篇的重点。

我们发现,在朴素算法中每次两个点向上跳的过程会耗费大量的时间,因此可以考虑对其进行优化。
那么具体该如何实现呢?显然,可以考虑使用倍增。不了解倍增原理的可以自行在oi-wiki上查阅。
我们在找LCA前先预处理好一个二维数组\(fa_{x,i}\),它表示结点x的第\(2^i\)个祖先。这样便可以实现了。

过程

首先将我们要用到的数组等信息定义好:

const int N=2*1e5+10; 
struct Node{
    int u,v,nxt;
}edge[N];
int cnt=0;
int fa[N][32],dep[N],lg[N];
int head[N];//采用链式前向星存图
void add(int u,int v)
{
	edge[++cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
void getlog(int n)
{
    for(int i=1;i<=n;i++)
    {
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
}

首先依旧要dfs整棵树,预处理好dep数组以及fa数组,而其中\(dep_i\)表示的是第i个结点的深度,fa数组在上文已经解释过。

void dfs(int now,int fath)//now为当前结点,fath为当前结点的父结点
{
    dep[now]=dep[fath]+1;//深度++;
    fa[now][0]=fath;//now结点的第2^0个祖先,即第一个祖先为fath
    for(int i=1;i<=lg[dep[now]];i++)
    {
        fa[now][i]=fa[fa[now][i-1]][i-1];//此处是倍增思想的体现,思考一下为什么
    }
    for(int i=head[now];i;i=edge[i].next)
    {
        if(edge[i].v!=fath) dfs(edge[i].v,now);//如果不重复就继续向下遍历
    }
}

到这里,我们的预处理就完成了,那么该如何来寻找两个结点的LCA呢?很简单,我们只需要先将两个点跳至同一深度,再用我们预先处理好的fa数组来向上找就OK了。具体可以结合代码

int LCA(int x,int y)
{
	if(dep[x]<dep[y])
	    swap(x,y);//为了方便处理,默认x的深度大于y
	while(dep[x]>dep[y])
	    x=fa[x][lg[dep[x]-dep[y]]-1];//向上跳,将两个点调整至同一深度
	if(x==y) return x;//若相等则直接返回
	for(int k=lg[dep[x]]-1;k>=0;k--)
	{
		if(fa[x][k]!=fa[y][k])
		{
			x=fa[x][k];
			y=fa[y][k];
		}//将二者同时向上跳,最多跳lg[dep[x]]-1次
	}
	return fa[x][0];//返回此时x父节点的值,即x与y的LCA
}

贴一份完整代码

#include<bits/stdc++.h>
#define life Elaina
#define angle Exusiai
#define int long long
using namespace std;

const int N=6*1e5+10;

struct Node{
	int u,v,nxt;
}edge[N*2];
int cnt=0;
int fa[N][32],dep[N],lg[N];
int head[N];

void add(int u,int v)
{
	edge[++cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}

void getlog(int n)
{
	for(int i=1;i<=n;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	}
}

void dfs(int now,int fath)
{
    dep[now]=dep[fath]+1;
	fa[now][0]=fath;
	for(int i=1;i<=lg[dep[now]];i++)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];
	}
	for(int i=head[now];i;i=edge[i].nxt)
	{
		if(edge[i].v!=fath) dfs(edge[i].v,now);
	}
}

int LCA(int x,int y)
{
	if(dep[x]<dep[y])
	    swap(x,y);
	while(dep[x]>dep[y])
	    x=fa[x][lg[dep[x]-dep[y]]-1];
	if(x==y) return x;
	for(int k=lg[dep[x]]-1;k>=0;k--)
	{
		if(fa[x][k]!=fa[y][k])
		{
			x=fa[x][k];
			y=fa[y][k];
		}
	}
	return fa[x][0];
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,m,s;
	cin>>n>>m>>s;
    for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	getlog(n);
	dfs(s,0);
    for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		cout<<LCA(a,b)<<endl;
	}
	return 0;
}

本人的第一篇博客,轻点喷

标签:dep,cnt,fa,祖先,结点,笔记,int,LCA
From: https://www.cnblogs.com/wolves487/p/18389609

相关文章

  • WQS 二分学习笔记
    1.股票买卖问题1.11.0版本考虑现在有\(n\)天,每天的股票价格\(a_i\)已知。你手上同时只能持有至多一张股票,且一笔买卖需要支付\(c\)的手续费。求最大收益。1.1.1解法1:DP我们不妨设\(f(i,0/1)\)表示前\(i\)天结束后手上是否持有股票。转移非常简单:\[f(i,0)=\m......
  • mini-lsm通关笔记Week2Day1
    项目地址:https://github.com/skyzh/mini-lsm个人实现地址:https://gitee.com/cnyuyang/mini-lsmSummary在本章中,您将:要将测试用例复制到启动器代码中并运行它们,实现合并某些SST文件并生成新SST文件的compaction逻辑。实现逻辑以更新LSM状态并管理文件系统上的SST文件。......
  • CG学习笔记 / 创建窗口、消息循环、窗口消息
    #include<Windows.h>LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,WPARAMwParam,LPARAMlParam){ switch(msg) { caseWM_CLOSE: PostQuitMessage(69);//exitCode->wParam break; caseWM_KEYDOWN: if(wParam=='F') { Set......
  • BinLLM论文阅读笔记
    Text-likeEncodingofCollaborativeInformationinLargeLanguageModelsforRecommendation论文阅读笔记Abstract现存的问题:​ 在调整用于推荐的大型语言模型(LLMRec)时,整合协作信息至关重要。现有的方法通过从头开始学习LLM潜在空间中的协作嵌入或通过外部模型的映射来......
  • java基础 -线程(基础)的 笔记
     581,多线程机制 因为需要敌人的坦克可以自由移动并发射子弹,我们的坦克可以移动并发射子弹,这些要用到线程的知识。   根据JConsole监控线程执行情况,发现,主线程执行完了,子线程还没有执行完,并不能表示当前进程死亡了,只有当所有的子线程执行完了,主进程才会结束。 真正......
  • 数据库学习笔记(黑马-Javaweb课程)
    概述P80.课程介绍:数据库:存储和管理数据的仓库SQL:操纵做关系型数据库的编程语言数据库管理系统:DBMS,操纵和管理数据库的大型软件课程介绍:数据的的设计,数据库的操作,数据库的优化-索引P81.MySQL-概述-安装配置图文详述:MySQL的下载、安装、配置、使用_mysql下载-CSDN博客语......
  • 伙伴匹配系统学习笔记(知识星球-鱼皮)
    一.需求分析用户去添加标签,标签的分类(要有哪些标签、怎么把标签分类)主动搜索:允许用户根据标签去搜索其他用户组队创建队伍加入队伍‘根据标签查询队伍邀请其他人允许用户修改标签推荐相似度计算算法+本地分布式计算二.技术栈后端Java编程语言+SpringBoot框架Spring......
  • 《数据资产管理核心技术与应用》读书笔记-第四章:数据质量的技术实现(三)
    《数据资产管理核心技术与应用》是清华大学出版社出版的一本图书,全书共分10章,第1章主要让读者认识数据资产,了解数据资产相关的基础概念,以及数据资产的发展情况。第2~8章主要介绍大数据时代数据资产管理所涉及的核心技术,内容包括元数据的采集与存储、数据血缘、数据质量、数据监控与......
  • Java基础-学习笔记17
    17IO流1.IO流文件文件在程序中是以流的形式来操作的。流:数据在数据源(文件)和程序(内存)之间经历的路径输入流:数据从数据源(文件)到程序(内存)的路径输出流:数据从程序(内存)到数据源(文件)的路径常用的文件操作获取文件的相关信息IO流原理及流的分类I/O(Input/Output......
  • 《基于超声的深度学习模型用于降低BI-RADS 4A乳腺病变的恶性率》论文笔记 MobileNet
    《APPLICATIONOFDEEPLEARNINGTOREDUCETHERATEOFMALIGNANCYAMONGBI-RADS4ABREASTLESIONSBASEDONULTRASONOGRAPHY》《基于超声的深度学习模型用于降低BI-RADS4A乳腺病变的恶性率》原文地址:链接文章目录摘要简介方法患者图像获取与处理深度学习模型统计分析结果讨论......