首页 > 其他分享 >【模板】最近公共祖先LCA——倍增

【模板】最近公共祖先LCA——倍增

时间:2023-09-11 20:59:19浏览次数:33  
标签:leq int 公共 fa depth 祖先 LCA 倍增 模板

题目来自洛谷P3379 【模板】最近公共祖先(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:应要求加了两组数据卡掉了暴力跳。

最近公共祖先——树上倍增

预处理(倍增) \(O(nlogn)\)

\(fa[i, j]\)表示从\(i\)结点出发,向上跳\(2^j\)步所抵达的节点编号;
求解方式:
\(j = 0, fa[i, 0] = i的父节点\)
\(j > 0\), \(i\)向上跳\(2^{i-1}\)步后再跳\(2^{i-1}\)步就可以跳到\(2^i\)的位置,所以\(\forall 0 < j \leq Max\),$$fa[i, j] = fa[fa[i, j-1], j-1]$$
\(depth[i]\)表示结点\(i\)的深度;
哨兵: \(depth[0] = 0; fa[i, j]=0\), 表示\(i\)向上跳\(2^j\)步跳出根结点的范围。
实现方式: \(DFS或BFS\)

查询 \(O(log n)\)

查询分两步走:
(\(1\))先将两个点跳到同一层
该步骤可以使用二进制拼凑的方式来实现。假设\(depth[x] > depth[y]\),则两者之间的差距为\(k = depth[x] - depth[y]\),由于是按照\(2\)的整数幂向上跳,所以可以通过取\(k\)的二进制表示中\(1\)所在的位对应的权值进行跳跃即可将\(x\)跳到与\(y\)同一层。
在实现过程中,可以从大到小枚举指数\(e\),当\(depth[fa[x][e]] >= depth[y]\)时,\(x=fa[x][e]\), 这样当\(x\)跳到与\(y\)同一层时就会停止跳跃。
如果在这一步后\(x == y\), 则直接返回\(x\).

(\(2\))两个点同时向上跳,直到跳到最近公共祖先的下一层
为什么是下一层?因为如果直接判断是否跳到同一个节点,无法判断是否为最近的公共祖先,而有可能只是一个公共祖先。
判断条件: 从大到小枚举指数\(i\), \(fa[x, i] \neq fa[y, i]\), \(x = fa[x, i], \, y = fa[y, i]\)。
当\(fa[x, i] == fa[y, i]\)时,证明此时\(x, y\)已经跳到了最近公共祖先的下一层, 此时\(f[x][0]\)或\(f[y][0]\)就是答案。

#include<bits/stdc++.h>

const int N = 500050;

int n, m, s;
std::vector<std::vector<int>> g(N);
int f[N][22];
int depth[N];

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

	while(!q.empty()){
		auto u = q.front();
		q.pop();

		for(auto v:g[u]){
			if(depth[v] > depth[u] + 1){
				depth[v] = depth[u] + 1;
				f[v][0] = u;
				q.push(v);
				for(int k = 1; k <= 20; k ++ ){
					f[v][k] = f[f[v][k - 1]][k - 1];
				}
			}
		}
	}
}

int lca(int a, int b){
	if(depth[a] < depth[b]) std::swap(a, b);

	for(int k = 20; k >= 0; k -- ){
		if(depth[f[a][k]] >= depth[b]){
			a = f[a][k];
		}
	}
	if(a == b) return a;
	for(int k = 20; k >= 0; k -- ){
		if(f[a][k] != f[b][k]){
			a = f[a][k];
			b = f[b][k];
		}
	}

	return f[a][0];
}

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	std::cin >> n >> m >> s;
	for(int i = 0; i < n - 1; i ++ ){
		int x, y;
		std::cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}

	pre(s);

	while(m -- ){
		int a, b;
		std::cin >> a >> b;
		int anc = lca(a, b);
		std::cout << anc << '\n';
	}

	return 0;
}

标签:leq,int,公共,fa,depth,祖先,LCA,倍增,模板
From: https://www.cnblogs.com/yjx-7355608/p/17694445.html

相关文章

  • 线性基模板
    插入第\(k\)小排名最大异或和structbasis{ vector<ll>s; voidinsert(llval) { for(intx:s)val=min(val,val^x); for(int&x:s)x=min(x,x^val); if(val)s.push_back(val); } llkth(llk) { sort(s.begin(),s.end()); if(s.size()<n)k--; ......
  • index.html在webpack打包时动态生成index模板
    通过<%=BASE_URL%>包裹环境变量通过<%if(process.env.NODE_ENV==='production'){%><%}%>包裹条件判断<!DOCTYPEhtml><html><head><metacharset="utf-8"/><metacontent="IE=edge,chrome=1&qu......
  • 【学习笔记】【自学】【模板】矩阵快速幂
    题目描述:给定$n\timesn$的矩阵$A$,求$A^k$。矩阵:一个$m\timesn$的矩阵是一个由$m$行$n$列元素排列成的矩形阵列。即形如$$A=\begin{bmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\vdots&\vdots&......
  • 【学习笔记】【自学】【模板】三分法
    题目描述:给定一个$n$次函数$f(x)$形如$a_1x^n+a_{2}x^{n-1}+......+a_{n-1}x^2+a_nx+a_{n+1}$,求$f(x)_{\max}$,且$x\in[l,r]$,设使得$f(x)_{\max}$的$x$为$x_{\max}$。对于一个区间$[l,r]$而言,若确定使得$f(x)$为最大值的$x$定在$[l,r]$中,则可以使用三分法求......
  • chrome插件:一个基于webpack + react的chrome 插件项目模板
    项目结构$tree-L1.├──README.md├──node_modules#npm依赖├──package.json#详细依赖├──pnpm-lock.yaml├──public#里边包含dist,安装的时候安装这个目录即可├──src#源码文......
  • 支持 range-based for 循环的链式前向星模板
    众所周知,OI中图的存储主要有两种方式:使用std::vector实现的邻接表,以及链式前向星。前者的常数较大,有时候会出现卡不过去的情况,但是支持range-basedfor循环,遍历的时候代码更简洁。可不可以在使用链式前向星的同时,使用range-basedfor循环呢?如以下代码所示:Graphgraph;int......
  • 倍增求lca
    步骤:1.前置准备:lg数组,depth数组,fa数组2.若x与y不在同一深度,先让它们跳到同一深度3.开始倍增往上跳代码:#include<iostream>#include<cstdio>usingnamespacestd;constlonglongN=1e6+10;intn,m,s,h1,h2,lg[N],fa[500010][30],depth[N];inttot=0,head[N],nxt[N],v[N......
  • AC自动机模板
    Smiling&Weeping----自从我们相遇的那一刻,你是我白天黑夜不落的星 题目链接:Problem-2222(hdu.edu.cn)题目就是一道AC自动机模板Talkischeap,showmethecode1#include<iostream>2#include<cmath>3#include<cstring>4#i......
  • PHP 网页扫码登录 , 推送模板消息
    缘由:因为老板要做个PC端的微信扫码绑定登录,关注公众号,推送模板消息的功能框架:ThinkPHP5功能:实现扫码微信公众号授权登录绑定,推送模板消息1.正式配置准备:微信公众号(必须申请了服务号) Appid, AppSecret配置:微信公众平台修改: 授权回调地址域名......
  • 模板模式(template)
    模板模式(Template)1、作用做一件是的方法很多,但做这件都可以归纳为几个步骤。这个时候可以使用模板模式,在模板类中,定义做事的步骤,将多种实现做事的细节延迟到子类中去实现。即:定义一个操作中的算法的骨架(模板函数),而将一些步骤延迟到子类中(基本函数)。模板方法使得子类可以不改变......