LCA浅谈

LCA定义

  • 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u和v的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先。
  • 另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。
    这里给出一个LCA的例子:
    对于T=<V,E>
    UczSED.png
V={1,2,3,4,5}
E={(1,2),(1,3),(3,4),(3,5)}
则有:
LCA(T,5,2)=1
LCA(T,3,4)=3
LCA(T,4,5)=3

倍增-LCA 原理

  • 利用倍增的思想,由两个同样深度的点,由上 2 n 2^n 2n的点进行向下的 2 n − i 2^{n-i} 2ni的点的搜索.

化深度统一

O(n)算法

  • 由最深的点向上跑到另一个点 .

O(lng_2{n})算法[倍增]

  1. 由最深的点 2 t 2^t 2t ( 2 t + 1 > n , 2 t < = n ) (2^{t+1}>n ,2^t <= n ) (2t+1>n,2t<=n) 2 0 2^0 20循环;
  2. 如果能加就累加上去直到高度差为 0 0 0.

搜索公共祖先

O(n)算法

  • 两个点同时向上跑,直到是同一个点.

O(log_2{n})算法[倍增]

  1. 两点从他们上面第 2 t 2^t 2t的点向下直到 2 0 2^0 20点搜索;
  2. 直到该点不是两者的公共祖先,在用化深度统一的方法找到他们的LCA.

倍增-LCA代码实现

1.定义:

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#define maxn 100039
using namespace std;
int deep[maxn], f[maxn][21], t;
int KK;
struct FLY{
	int to, nex;
}edges[maxn<<1];
int head[maxn];
int n, Q;

2.建树:

void add(int from, int to){
	edges[KK].to = to;
	edges[KK].nex = head[from];
	head[from] = KK++;
}

3.广搜:

void bfs(){
	queue<int> q;
	q.push(13), deep[13] = 1;
	while(!q.empty()){
		int x = q.front();q.pop();
		for(int i = head[x]; i != -1; i = edges[i].nex){
			int y = edges[i].to;
			if(deep[y])continue;  //不走父节点 
			deep[y] = deep[x]+1;  //处理深度 
			f[y][0] = x;  //赋初值 
			for(int j = 1; j < t+1; j++)  //倍增DP核心 
				f[y][j] = f[f[y][j-1]][j-1];
			q.push(y);
		}
	}
}

4.LCA(核心):

int lca(int x, int y){
	if(deep[x]>deep[y]){
		int tmp = x;x = y; y = tmp;
	}
	for(int i = t; i > -1; i--)
		if(deep[f[y][i]]>=deep[x])y = f[y][i];  //移到同一深度 
	if(x==y)return x;
	for(int i = t; i > -1; i--)
		if(f[x][i]!=f[y][i])x = f[x][i], y = f[y][i];  //lca过程 
	return f[x][0];  //最后停在lca的子节点,所以返回父节点 
}

5.主程序:

int main(){
	freopen("1.in", "r", stdin);
	scanf("%d", &n);
	memset(head, -1, sizeof(head));
	int a, b;
	for(int i = 1; i < n; i++){
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	t = (int)log2(n)+1;
	bfs();
	scanf("%d", &Q);
	while(Q--){
		scanf("%d%d", &a, &b);
		printf("%d\n", lca(a, b));
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值