一组人的所有共同管理者在它们 LCA 到根的路径上,答案是这条路径上的最大编号。
P10113 [GESP202312 八级] 大量的工作沟通
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
只输出 LCA 会漏掉 LCA 上方编号更大的管理者。
二、核心观察
多点 LCA 得到最近共同管理者,max_path[L] 存 L 到根路径最大编号。
为什么不丢答案:共同祖先集合正好是 LCA 的所有祖先,所以只需在这条祖先链上取编号最大。
当前 LCA
祖先链
最大编号
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
主持人一定是谁的什么?
先判断,再点按钮。
为什么不是选深度最大?
先判断,再点按钮。
五、手推结果
| 查询 | 处理 |
|---|---|
| 多人 | 两两滚动求 LCA |
| 候选 | LCA 到根 |
| 答案 | max_path[LCA] |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int parent[MAXN];
int depth[MAXN];
int fa[MAXN][20];
int max_path[MAXN];
vector<int> children[MAXN];
// 预处理深度和倍增表
void bfs(int n) {
queue<int> q;
q.push(0);
depth[0] = 1;
max_path[0] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : children[u]) {
depth[v] = depth[u] + 1;
fa[v][0] = u;
max_path[v] = max(v, max_path[u]); // 关键:路径最大值
for (int i = 1; i < 20; i++) {
fa[v][i] = fa[fa[v][i - 1]][i - 1];
}
q.push(v);
}
}
}
// 求两个点的 LCA
int get_lca(int u, int v) {
if (depth[u] < depth[v]) swap(u, v);
for (int i = 19; i >= 0; i--) {
if (depth[fa[u][i]] >= depth[v]) {
u = fa[u][i];
}
}
if (u == v) return u;
for (int i = 19; i >= 0; i--) {
if (fa[u][i] != fa[v][i]) {
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i < n; i++) {
cin >> parent[i];
children[parent[i]].push_back(i);
}
bfs(n);
int q;
cin >> q;
while (q--) {
int m;
cin >> m;
int current_lca;
for (int i = 0; i < m; i++) {
int node;
cin >> node;
if (i == 0) {
current_lca = node;
} else {
current_lca = get_lca(current_lca, node);
}
}
// 题目要求:在 LCA 到根的路径上找编号最大的点
cout << max_path[current_lca] << "\n";
}
return 0;
}