最近公共祖先

最近公共祖先( L e a s t   C o m m o n   A n c e s t o r s \rm Least\,Common\,Ancestors LeastCommonAncestors),简记为 L C A \rm LCA LCA。顾名思义就是一棵树中的某两个节点的公共的祖先中离他们最近,即深度最大的那个

举个例子:

上图中 8 8 8 6 6 6 的 LCA 就是 1 1 1

那么怎么求 LCA 呢?

1. 向上标记法

思路十分简单。

我们现在要求 LCA(x,y) ⁡ \operatorname{LCA(x,y)} LCA(x,y)。首先从 x x x 向上到根节点 r t rt rt,经过的每一个节点都打上标记。然后从 y y y 向上到 r t rt rt,遇到的第一个有标记的节点就是 L C A LCA LCA

代码实现很简单,就不给了。

2. 同步前进法

LCA(x,y) ⁡ \operatorname{LCA(x,y)} LCA(x,y) 的具体方法为:

  1. 先让 x x x 的深度大于或等于 y y y。这相当于数学上的一个假设: d e p ( x ) ≥ d e p ( y ) dep(x)\ge dep(y) dep(x)dep(y) d e p dep dep 代表深度)
  2. 不停地将 x x x 往上跳,直到 x x x y y y 深度相等。
  3. 特判:若 x = y x=y x=y L C A LCA LCA 就是 x x x 了。
  4. 否则在保证 f a ( x ) ≠ f a ( y ) fa(x)\ne fa(y) fa(x)=fa(y) f a fa fa 代表父亲) 的情况下 x x x y y y 同时向上跳一格。
  5. L C A LCA LCA 就是 x x x 的父亲。

以上面的 8 8 8 6 6 6 为例:

  1. d e p ( 8 ) > d e p ( 6 ) dep(8)>dep(6) dep(8)>dep(6),无需交换。
  2. 8 8 8 跳到 4 4 4 d e p ( 4 ) = d e p ( 6 ) dep(4)=dep(6) dep(4)=dep(6)
  3. 4 ≠ 6 4\ne6 4=6
  4. f a ( 4 ) = 2 ≠ f a ( 6 ) = 3 fa(4)=2\ne fa(6)=3 fa(4)=2=fa(6)=3 4 4 4 跳到 2 2 2 6 6 6 跳到 3 3 3 f a ( 2 ) = 1 = f a ( 3 ) fa(2)=1=fa(3) fa(2)=1=fa(3),停止。
  5. LCA(8,6) ⁡ = f a ( 2 ) = 1 \operatorname{LCA(8,6)}=fa(2)=1 LCA(8,6)=fa(2)=1

在此之前还需 dfs 一遍求出 d e p dep dep f a fa fa

void dfs(int u, int father)
{
	fa[u] = father;
	dep[u] = dep[father] + 1; //u深度即father深度加1
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v != father)
		{
			dfs(v, u);
		}
	}
}

int lca(int x, int y)
{
	if (dep[x] < dep[y])
	{
		swap(x, y); //让x的深度大于或等于y
	}
	while (dep[x] > dep[y])
	{
		x = fa[x]; //向上跳
	}
	if (x == y)
	{
	  	return x; //特判
	}
	while (fa[x] != fa[y])
	{
		x = fa[x]; //同时跳
		y = fa[y];
	}
	return fa[x]; //LCA是父亲
}

但以上两种方法时间复杂度均为 O ⁡ ( n m ) \operatorname{O}(nm) O(nm)……

然后洛谷 A 了。

对于 100 % 100\% 100% 的数据, N ≤ 500000 N\le500000 N500000 M ≤ 500000 M\le500000 M500000

数据太水了啊啊啊!!!

出数据的真良心。。。

3. 倍增

上面暴力算法显然过慢,所以我们要使用倍增算法,也是一种空间换时间的策略。

先预处理出 l g lg lg 数组,用来保存 ⌊ l o g 2 x ⌋ \left\lfloor log_2x\right\rfloor log2x

for (int i = 2; i <= n; i++)
{
	lg[i] = lg[i >> 1] + 1;
}

f a ( x ) ( i ) fa(x)(i) fa(x)(i) x x x 的第 2 i 2^i 2i 级祖先,dfs 时要算 f a ( u ) ( i ) fa(u)(i) fa(u)(i)

LCA 的第 2 2 2 步, x x x 直接向上跳 l g ( d e p ( x ) − d e p ( y ) ) lg(dep(x)-dep(y)) lg(dep(x)dep(y)) 格。第 4 4 4 步时,我们遍历 i = l g ( d e p ( x ) ) + 1 ∼ 0 i=lg(dep(x))+1\sim0 i=lg(dep(x))+10,每次 x x x y y y 同时向上跳 2 i 2^i 2i 格。

void dfs(int u, int father)
{
	fa[u][0] = father; //u的第1级祖先就是父亲
	dep[u] = dep[father] + 1;
	for (int i = 1; i <= lg[dep[u]] + 1; i++)
	{
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
	} //算fa(u)(i)
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v != father)
		{
			dfs(v, u);
		}
	}
}

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]]];
	}
	if (x == y)
	{
	  	return x;
	}
	for (int i = lg[dep[x]] + 1; i >= 0; i--)
	{
		if (fa[x][i] != fa[y][i])
		{
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];
}

复杂度分析:

时间空间
暴力预处理 O ⁡ ( n ) \operatorname{O}(n) O(n),每次询问 O ⁡ ( n ) \operatorname{O}(n) O(n) O ⁡ ( n ) \operatorname{O}(n) O(n)
倍增预处理 O ⁡ ( n log ⁡ n ) \operatorname{O}(n\log n) O(nlogn),每次询问 O ⁡ ( log ⁡ n ) \operatorname{O}(\log n) O(logn) O ⁡ ( n log ⁡ n ) \operatorname{O}(n\log n) O(nlogn)

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

暴力:

倍增:

用 LCA 求树上两点间的最短距离

还是以这张图为例主要是我懒

8 8 8 5 5 5

预处理出每个点到根节点的距离 d i s ( x ) dis(x) dis(x),这里因为是无权边, d i s ( x ) dis(x) dis(x) 直接就是 d e p ( x ) dep(x) dep(x) 了。

先用 d i s ( 8 ) dis(8) dis(8) 加上 d i s ( 5 ) dis(5) dis(5),此时多加了 2 2 2 d i s ( 2 ) dis(2) dis(2),故 8 8 8 5 5 5 的距离就应该是 d i s ( 8 ) + d i s ( 5 ) − 2 × d i s ( 2 ) dis(8)+dis(5)-2\times dis(2) dis(8)+dis(5)2×dis(2),即树上 x x x y y y 两点间的最短距离为 d i s ( x ) + d i s ( y ) − 2 × d i s ( L C A ) dis(x)+dis(y)-2\times dis(LCA) dis(x)+dis(y)2×dis(LCA)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值