Acwing 1172.祖孙询问

题目描述
给定一棵包含 n 个节点的有根无向树,节点编号互不相同,但不一定是 1∼n。

有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系。

输入格式
输入第一行包括一个整数 表示节点个数;

接下来 n 行每行一对整数 a 和 b,表示 a 和 b 之间有一条无向边。如果 b 是 −1,那么 a 就是树的根;

第 n+2 行是一个整数 m 表示询问个数;

接下来 m 行,每行两个不同的正整数 x 和 y,表示一个询问。

输出格式
对于每一个询问,若 x 是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0。

数据范围
1≤n,m≤4×104,
1≤每个节点的编号≤4×104
输入样例:

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

思路:
采用倍增法
在读入所有的边后,使用bfs来初始化depth和fa数组。
其中 f a [ i ] [ j ] fa[i][j] fa[i][j]表示i节点向上跳 2 j 2^j 2j后所能到达的点。
d e p t h [ i ] depth[i] depth[i]表示i节点的深度。我们规定根节点的深度为1。

在bfs函数中,使用广度优先遍历从根节点遍历每一个点,针对点t,如果发现t的邻接点的深度大于t的深度+1,那么就认为该节点从未到达过。(因为t的邻接点的深度最大为t的深度+1,而所有节点的初始化深度为正无穷)。如果发现该邻接点从未到达过,那么就更新depth和fa数组,并入队。
针 对 j 点 的 d e p t h 数 组 , 由 于 其 是 t 的 邻 接 点 , 同 时 t 距 离 r o o t 更 近 , 故 j 的 d e p t h 设 置 为 d e p t h [ t ] + 1 。 针对j点的depth数组,由于其是t的邻接点,同时t距离root更近,故j的depth设置为depth[t]+1。 jdepthttrootjdepthdepth[t]+1
针 对 j 点 的 f a 数 组 , 由 于 其 向 上 的 点 都 已 被 遍 历 过 , 故 可 以 直 接 更 新 f a 数 组 。 针对j点的fa数组,由于其向上的点都已被遍历过,故可以直接更新fa数组。 jfafa
j j j点的fa数组更新方式如下:
已 知 f a [ j ] [ k ] 表 示 j 节 点 向 上 走 2 k 步 所 能 到 达 的 的 点 , 那 么 f a [ j ] [ k ] = f a [ f a [ j ] [ k − 1 ] ] [ k − 1 ] 即 j 节 点 向 上 走 2 ( k − 1 ) 步 所 能 到 达 的 的 点 再 向 上 走 2 ( k − 1 ) 步 所 能 到 达 的 点 已知fa[j][k]表示j节点向上走2^k步所能到达的的点,那么fa[j][k]=fa[fa[j][k-1]][k-1]即j节点向上走2^{(k-1)}步所能到达的的点再向上走2^{(k-1)}步所能到达的点 fa[j][k]j2kfa[j][k]=fa[fa[j][k1]][k1]j2(k1)2(k1)

在lca函数中,首先将a设置为深度更深的点
然后将a节点向上跳,直到a与b处于同一深度。(只要a的深度大于等于b就要往上跳)
当a,b处于同一深度时,判断a是否等于b,若相等,则a与b 的公共祖先为a。
若不等则,两者向上跳,只有两者的fa不等的时候才向上跳。
比如两者的公共祖先在向上数8的地方。那么k=3时,就不会跳。而在k=2,1,0的时候会跳,相当于跳了7步,此时两者的共同祖先一定是 f a [ a / b ] [ 0 ] fa[a/b][0] fa[a/b][0]

代码

#include<queue>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
int e[N], ne[N], depth[N], h[N];
int fa[N][16];
int idx = 0;
void bfs(int root)
{
	depth[0] = 0;
	depth[root] = 1;
	queue<int>q;
	q.push(root);
	while (!q.empty())
	{
		int t = q.front();
		q.pop();
		for (int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (depth[j] >= depth[t] + 1)
			{
				depth[j] = depth[t] + 1;
				q.push(j);
				fa[j][0] = t;
				for (int k = 1; k < 16; k++)
				{
					fa[j][k] = fa[fa[j][k - 1]][k - 1];
				}
			}
		}
	}
}
void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx;
	idx++;
}
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()
{
	memset(h, -1, sizeof h);
	memset(depth, 0x3f3f3f3f, sizeof depth);
	int n;
	cin >> n;
	int root = 0;
	for (int i = 0; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		if (b != -1)
		{
			add(a, b);
			add(b, a);
		}
		else
		{
			root = a;
		}
	}
	bfs(root);
	int m;
	cin >> m;
	while (m--)
	{
		int a, b;
		cin >> a >> b;
		int p = lca(a, b);
		if (a == p)
		{
			cout << 1 << endl;
		}
		else if (b == p)
		{
			cout << 2 << endl;
		}
		else
		{
			cout << 0 << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值