P10113 [GESP202312 八级] 大量的工作沟通

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

一组人的所有共同管理者在它们 LCA 到根的路径上,答案是这条路径上的最大编号。

容易误入的路

只输出 LCA 会漏掉 LCA 上方编号更大的管理者。

二、核心观察

建模与压缩

多点 LCA 得到最近共同管理者,max_path[L] 存 L 到根路径最大编号。

为什么不丢答案:共同祖先集合正好是 LCA 的所有祖先,所以只需在这条祖先链上取编号最大。

当前 LCA 祖先链 最大编号

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

主持人一定是谁的什么?

问题 2

为什么不是选深度最大?

五、手推结果

查询处理
多人两两滚动求 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;
}