Acwing_1172祖孙询问【最近公共祖先(倍增法)】

题目描述:
在这里插入图片描述
输入样例:

10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19

输出样例:

1
0
0
0
2

思路分析:
求最近公共祖先有两种方法:

  • 向上标记法
  • 倍增法

此题用的是倍增法,具体思路如下:

(1)fa[i][j] 表示从i开始,向上走2^j步所能走到的节点 0<=j<=logn
(2)depth[i]表示深度
(3)哨兵:如果从i开始跳2^j步会跳过根节点,那么fa[i][j]=0,depth[0]=0

步骤:
【1】先将两个点跳到同一层
【2】让两个点同时往上跳,一直跳到它们的最近公共祖先的下一层

时间复杂度:

  • 预处理:O(nlogn)
  • 查询O(logn)

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;

const int N = 40010, M = N * 2;

int n, m;
int h[N], e[M], ne[M], idx;
int depth[N], fa[N][16];
int q[N];//手写队列

void add(int a,int b) {
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;

}

void bfs(int root) {
	memset(depth, 0x3f, sizeof depth);
	depth[0] = 0, depth[root] = 1;
	int hh = 0, tt = 0;
	q[0] = root;
	while (hh <= tt) {
		int t = q[hh++];
		for (int i = h[t]; i != 0; i = ne[i]) {
			int j = e[i];
			if (depth[j] > depth[t] + 1) {
				depth[j] = depth[t] + 1;
				q[++tt] = j;
				fa[j][0] = t;
				for (int k = 1; k <= 15; k++) {
					fa[j][k] = fa[fa[j][k - 1]][k - 1];//基于递推法
				}
			}
		}
	}

}

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

	if (a == b) return a;

	for (int k = 15; k >= 0; k--) {
		if (fa[a][k] != fa[b][k]) {
			a = fa[a][k];
			b = fa[b][k];
		}
	}
	return fa[a][0];
}

int main()
{
	scanf("%d", &n);

	int root = 0;
	memset(h, -1, sizeof h);

	for (int i = 0; i < n; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		if (b == -1) root = a;
		else add(a, b), add(b, a);
	}

	bfs(root);

	scanf("%d", &m);
	while (m--) {
		int a, b;
		scanf("%d%d", &a, &b);
		int p = lca(a, b);
		if (p == a) puts("1");
		else if (p == b) puts("2");
		else puts("0");
	}

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值