首页 > 其他分享 >【笔记/模板】最近公共祖先(LCA)

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

时间:2024-11-04 10:33:06浏览次数:1  
标签:pre int 复杂度 笔记 depth LCA 节点 模板

最近公共祖先(LCA)

定义

最近公共祖先(Lowest Common Ancestor)简称 LCA。对于一个树上的两个节点的最近公共祖先,是这两个点中的公共祖先里面离根最远的一个。性质可见 OI Wiki

向上标记法

过程

在两点中取得深度较大的一个点,让它不停的向上跳,同时标记所经过的每一个点,直到根节点,接着让另一个点不断向上跳,跳到的第一个被标记的点就是这两个点的 LCA。

时间复杂度

预处理树的深度的时间复杂度为 \(O(n)\),单次查询的时间复杂度最坏情况下为 \(O(n)\)。

倍增算法

过程

我们可以定义一个数组 \(pre_{i, k}\) 表示编号为 \(i\) 的点向上跳 \(2^k\) 次所到达的点。不难看出 \(pre_{i, 0}\) 表示的是节点 \(i\) 的父亲。对于任意的 \(k \ge 1\),\(pre_{i, k}\) 都相当于连续上跳两次 \(2^{k - 1}\),满足以下的递推式:

\[pre_{i, k} = pre_{pre_{i, k - 1}, k - 1} \]

因此根据每个节点的父亲就可以递推出整个 \(pre\) 数组。以上所有操作均可以在 dfs 或者 bfs 的初始化中实现。

假设我们求出了所有节点的 \(depth\) 数组。对于任意的两个节点 \(u,v\),它们的深度之差即为 \(h = depth_u - depth_v\) ,我们可以使用多重背包优化中的二进制拆分的思想,将 \(h\) 分为至多 \(\log h\) 个数,从而达到 \(O(\log h)\) 的时间复杂度进行预上跳。

进行预上跳之后,特判一下此时的两个节点是否已经相同。如果不同,从 \(pre\)​ 数组的第二维从大到小依次上跳找到第一个不是他们两个祖先的两个点,让两个点依次跳到这两个点。

最后所求的 \(\text{LCA}(u, v)\) 便是这两个点的父亲,即 \(pre_{u, 0}\)。

时间复杂度

dfs 或 bfs 进行预处理所花的时间复杂度均为 \(O(n)\),进行 \(pre\) 数组递推式处理的时间复杂度为 \(O(n \log n)\),单次询问的时间复杂度为 \(O(\log n)\)。

模板代码

bfs 预处理 + 递推

void bfs(int root)
{
	memset(depth, 0x3f, sizeof depth);
	depth[0] = 0, depth[root] = 1;
	queue<int> q;
	q.push(root);

	while (!q.empty())
	{
		int t = q.front(); q.pop();
		
		for (auto y : g[t])
		{
			if (depth[y] > depth[t] + 1)
			{
				depth[y] = depth[t] + 1;
				q.push(y);
				pre[y][0] = t;
				for (int k = 1; k <= 19; k ++)
					pre[y][k] = pre[pre[y][k - 1]][k - 1];
			}
		}
	}
}

dfs + 递推

void dfs(int ver, int fa, int dep)
{
	pre[ver][0] = fa;
	depth[ver] = dep;
	for (auto y : g[ver])
		if (y != fa)
			dfs(y, ver, dep + 1);
}

void init()
{
	for (int j = 1; i <= 19; ++j)
        for (int i = 1; i <= n; ++i)
        	pre[i][j] = pre[pre[i][j - 1]][j - 1];
}

求 LCA

int lca(int a, int b)
{
	if (depth[a] < depth[b]) swap(a, b);
	
	for (int k = 19; k >= 0; k --)
		if (depth[pre[a][k]] >= depth[b])
			a = pre[a][k];

	if (a == b) return a;

	for (int k = 19; k >= 0; k --)
		if (pre[a][k] != pre[b][k])
			a = pre[a][k], b = pre[b][k];
			
	return pre[a][0];
}

标签:pre,int,复杂度,笔记,depth,LCA,节点,模板
From: https://www.cnblogs.com/ThySecret/p/18523427

相关文章

  • 【笔记/模板】最小生成树
    www.luogu.com.cn概念/定义一个连通图的生成树是一个极小的连通子图,它包含图中全部的\(n\)个顶点,但只有构成一棵树的\(n-1\)条边。而最小生成树就是一个带权图的生成树,并且使得原图中边的权值最小的生成树,所谓最小是指边的权值之和小于或者等于其它生成树的边的权值之和。......
  • 【模板】Floyd算法
    Floyd算法原理Floyd算法用来求出任意两个节点之间的最短路。优点:代码少,思维简单,适用于除了负环以外的任何图。缺点:时间复杂度为\(O(n^3)\),空间复杂度为\(O(n^2)\)。而Floyd的核心原理是用动态规划实现的,定义一个二维数组\(f_{i,j}\),遍历图上的所有点\(k\),可以得......
  • 【模板】分块
    今天写分块的时候模板忘光了,故写以记之。CodeInitvoidinit(){sz=sqrt(n),block=n/sz+(n%sz!=0);for(inti=1;i<=block;i++)st[i]=(i-1)*sz+1,ed[i]=i*sz;ed[block]=n;for(inti=1;i<=block;i++)......
  • PbootCMS模板调用友情链接标签代码
    适用范围:全站任意地方标签作用:用于依次输出指定分组的友情链接调用代码:html {pboot:linkgid=*num=*}<ahref="[link:link]"title="[link:name]"><imgsrc="[link:logo]"></a>{/pboot:link}控制参数:gid=*:分组,必填num=*:数量,非必填,默认为10个可使用的列表......
  • PbootCMS模板调用幻灯片轮播图标签
    幻灯片轮播图列表:{pboot:slidenum=3gid=1}<ahref="[slide:link]"target="_blank"><imgsrc="[slide:src]"alt="[slide:title]"/></a>{/pboot:slide}控制参数:gid=*:分组,必填。num=*:数量,非必填,默认为5个。可用列表标......
  • pbootcms模板英文站搜索效果页面包屑显示优化
    打开 \apps\home\controller\SearchController.php 文件,根据版本替换代码:2.1.1版本:if(cookie('lg')=='cn'){//中文处理}else{//英文处理$content=str_replace('{pboot:pagetitle}',$this->config('search_title')?:......
  • GD32F1x模板工程的创建
    本文根据b站up主高博士_嵌入式的视频来写。视频链接:[2-3]05创建GD32F10x模板工程_哔哩哔哩_bilibili第一章:项目的创建首先创建一个文件夹,这个文件夹用来储存项目。  我们把项目的名字命名为test。  选择自己要使用的芯片,点击ok。 点击三个方块形状形成的按钮出......
  • Web 靶场笔记-bWAPP
    事以密成,言以泄败。导航前言A1-Injection(注入)A2-BrokenAuth&SessionMgmt(破损的授权&会话管理)A3-Cross-SiteScripting(XSS跨站脚本)A4-InsecureDirectObjectReferences(IDOR不安全直接对象引用)A5-SecurityMisconfiguration(安全配置错误)......
  • 《人件集》阅读笔记2(2024.11.1)
    一、章节内容梳理(一)第五章:环境与协作的交织这一章让我深刻认识到办公环境对软件开发人员的重要性。无论是物理空间的布局,还是设施的配备,都如同隐形的手影响着工作效率和团队沟通。从开发人员的角色定位角度看,合适的环境能让他们更清晰地明确自己在团队中的位置,更好地发挥专长。......
  • FFT学习笔记
    $\quad$本人蒟蒻,只能介绍FFT在OI中的应用,如有错误或不当之处还请指出。$\quad$首先先说一下那一堆什么什么\(TT\)的都是什么DET:离散傅里叶变换用于求多项式乘法\(O(n^2)\)FFT:快速傅里叶变换用于求多项式乘法\(O(nlog(n))\)FNTT/NTT:FTT的优化,常数及精度更优FWT......