跳到主要内容

最近公共祖先(LCA)

信息

此题目来自 洛谷, 原始题目与提交代码请前往 P3379 【模板】最近公共祖先(LCA) - 洛谷

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

第一行包含三个正整数 N,M,SN,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 N1N−1 行每行包含两个正整数 x,yx,y,表示 xx 结点和 yy 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 MM 行每行包含两个正整数 a,ba,b,表示询问 aa 结点和 bb 结点的最近公共祖先。

输出格式

输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入 #1

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出 #1

4
4
1
4
4

说明/提示

对于 30%30\% 的数据,N10{N}\le{10}M10{M}\le{10}

对于 70%70\% 的数据,N10000{N}\le{10000}M10000{M}\le{10000}

对于 100%100\% 的数据,1N,M5×105{1}\le{N,M}\le{5\times{{10}^{5}}}1x,y,a,bN{1}\le{x,y,a,b}\le{N}不保证 ab{a}\ne{b}

样例说明:

该树结构如下:

第一次询问:2,42,4 的最近公共祖先,故为 44

第二次询问:3,23,2 的最近公共祖先,故为 44

第三次询问:3,53,5 的最近公共祖先,故为 11

第四次询问:1,21,2 的最近公共祖先,故为 44

第五次询问:4,54,5 的最近公共祖先,故为 44

故输出依次为 4,4,1,4,44,4,1,4,4

题目解答

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAXN = 500005;

struct Query {
int b, id;
};

vector<int> graph[MAXN];
vector<Query> queries[MAXN];
int parent[MAXN], ancestor[MAXN];
bool visited[MAXN];
int ans[MAXN];

int find(int x) {
if (ancestor[x] == x) return x;
return ancestor[x] = find(ancestor[x]);
}

void unite(int x, int y) {
x = find(x);
y = find(y);
if (x != y) ancestor[y] = x;
}

void tarjan(int u) {
visited[u] = true;
ancestor[u] = u;

for (int v : graph[u]) {
if (visited[v]) continue;
tarjan(v);
unite(u, v);
ancestor[find(u)] = u;
}

for (const auto& q : queries[u]) {
if (visited[q.b]) {
ans[q.id] = find(q.b);
}
}
}

int main() {
ios::sync_with_stdio(false);
cin.tie(0);

int N, M, S;
cin >> N >> M >> S;

for (int i = 1; i < N; i++) {
int x, y;
cin >> x >> y;
graph[x].push_back(y);
graph[y].push_back(x);
}

// 存储查询
for (int i = 0; i < M; i++) {
int a, b;
cin >> a >> b;
queries[a].push_back({b, i});
queries[b].push_back({a, i});
}

// 初始化并查集
for (int i = 1; i <= N; i++) {
ancestor[i] = i;
}

tarjan(S);

for (int i = 0; i < M; i++) {
cout << ans[i] << '\n';
}

return 0;
}