洛谷P10113题(2023年GESP八级):用LCA算法高效解决大量的工作沟通

一、题目解读
洛谷P10113题(2023年GESP八级)要求解决树结构中多个节点的最近公共祖先(LCA)查询问题。给定一棵树及若干组节点,需快速找出每组节点的LCA。传统方法如暴力遍历时间复杂度较高,而本题采用倍增法(RMQ算法)优化,通过预处理实现O(logN)的查询效率,适用于大规模数据场景。
二、解题思路
核心是倍增法(基于ST表思想的变体),通过构建“倍增表”预处理每个节点的祖先信息。关键在于利用二进制拆分思想:
1. 预处理阶段:计算每个节点深度,构建up[i][j]表表示节点i的2^j层祖先。
2. 查询阶段:通过二分提升深度差异,再同步向上移动找到LCA。
此思路将单次查询复杂度降至O(logN),大幅优化性能。
三、解题步骤
1. 预处理阶段
● 初始化根节点(0号节点)深度为0,父节点为自身。
● 递归计算子节点深度:depth[i] = depth[parent[i]] + 1。
● 填充倍增表:up[i][j] = up[up[i][j-1]][j-1],即节点i的2^j祖先通过其2^(j-1)祖先的2^(j-1)祖先间接获得。
2. LCA查询
● 确保较深节点u与v深度对齐:通过二进制拆分快速提升u至与v同深度。
● 同步向上:从最高位开始,若u、v的2^j祖先不同,则同时向父节点移动。最终两者父节点即为LCA。
四、代码与注释
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
const int LOG = 20; // 足够大的对数级别
vector<int> parent(MAXN); // 存储每个节点的直接领导
vector<int> depth(MAXN); // 存储每个节点的深度
vector<vector<int>> up(MAXN, vector<int>(LOG)); // 倍增表
// 预处理函数,构建倍增表
void preprocess(int n) {
depth[0] = 0;
up[0][0] = 0; // 根节点的2^0祖先还是自己
for(int i = 1; i < n; ++i) {
depth[i] = depth[parent[i]] + 1;
up[i][0] = parent[i]; // 2^0祖先就是直接领导
}
for(int j = 1; j < LOG; ++j) {
for(int i = 0; i < n; ++i) {
up[i][j] = up[up[i][j-1]][j-1];
}
}
}
// 查找两个节点的LCA
int lca(int u, int v) {
if(depth[u] < depth[v]) swap(u, v); // 确保u更深
for(int j = LOG-1; j >= 0; --j) {
if(depth[u] - (1 << j) >= depth[v]) {
u = up[u][j]; // 提升u
}
}
if(u == v) return u; // u和v已重合
for(int j = LOG-1; j >= 0; --j) {
if(up[u][j]!= up[v][j]) {
u = up[u][j];
v = up[v][j];
}
}
return parent[u]; // 最终LCA为u的父节点
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
// 读取每个员工的直接领导
for(int i = 1; i < N; ++i) cin >> parent[i];
// 预处理倍增表
preprocess(N);
int Q; cin >> Q;
while(Q--) {
int m; cin >> m;
vector<int> employees(m);
for(int i = 0; i < m; ++i) cin >> employees[i];
// 找出所有员工的LCA
int current_lca = employees[0];
for(int i = 1; i < m; ++i) {
current_lca = lca(current_lca, employees[i]);
}
cout << current_lca << '\n';
}
}注释说明:
● up[i][j] 存储节点i的2^j层祖先,预处理通过迭代构建。
● lca(u, v) 函数通过二分法对齐深度,再同步向上查找LCA。
五、总结
本文通过洛谷P10113题的代码解析,展示了倍增法在LCA问题中的高效应用。该算法通过预处理空间换取查询时间,尤其适用于需要频繁查询LCA的场景。代码结构清晰,预处理与查询分离,可扩展性强。理解此解法有助于优化树结构相关算法设计,提升解题效率。
原创内容 转载请注明出处






