题目描述
给定一棵包含 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。
针对j点的depth数组,由于其是t的邻接点,同时t距离root更近,故j的depth设置为depth[t]+1。
针
对
j
点
的
f
a
数
组
,
由
于
其
向
上
的
点
都
已
被
遍
历
过
,
故
可
以
直
接
更
新
f
a
数
组
。
针对j点的fa数组,由于其向上的点都已被遍历过,故可以直接更新fa数组。
针对j点的fa数组,由于其向上的点都已被遍历过,故可以直接更新fa数组。
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]表示j节点向上走2k步所能到达的的点,那么fa[j][k]=fa[fa[j][k−1]][k−1]即j节点向上走2(k−1)步所能到达的的点再向上走2(k−1)步所能到达的点
在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;
}