P10725 [GESP202406 八级] 最远点对

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

一、先回答问题

结论

先求黑点集直径端点,再用这两个端点去覆盖最远异色距离。

容易误入的路

整棵树直径两个端点可能同色,直接输出会错。

二、核心观察

建模与压缩

点集直径端点能代表这个点集到任意点的最远距离候选。

为什么不丢答案:树上任一点到点集最远点,一定是该点集直径的某个端点。

B1 B2 最远白点

三、互动演示

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

四、自己选答案

问题 1

为什么不能只求整树直径?

问题 2

B1/B2 是什么?

五、手推结果

BFS 次数目的
1找黑点集端点 B1
2找 B2 并更新答案
3从 B2 更新答案

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
vector<int> adj[MAXN];
int color[MAXN];
int dist[MAXN];
int n;

// 通用的 BFS,计算从 start 到所有点的距离
void bfs(int start) {
    fill(dist, dist + n + 1, -1);
    queue<int> q;
    q.push(start);
    dist[start] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int v : adj[u]) {
            if (dist[v] == -1) {
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
}

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

    cin >> n;
    int first_black = -1;
    for (int i = 1; i <= n; i++) {
        cin >> color[i];
        if (color[i] == 1) first_black = i;
    }

    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    // 1. 找到黑色点集的一个直径端点 B1
    bfs(first_black);
    int b1 = first_black;
    int max_d = -1;
    for (int i = 1; i <= n; i++) {
        if (color[i] == 1 && dist[i] > max_d) {
            max_d = dist[i];
            b1 = i;
        }
    }

    // 2. 从 B1 出发,找到另一个直径端点 B2,同时算出 B1 到白点的最大距离
    bfs(b1);
    int b2 = b1;
    int max_d_black = -1;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (color[i] == 1 && dist[i] > max_d_black) {
            max_d_black = dist[i];
            b2 = i;
        }
        if (color[i] == 0) {
            ans = max(ans, dist[i]);
        }
    }

    // 3. 从 B2 出发,算出 B2 到白点的最大距离
    bfs(b2);
    for (int i = 1; i <= n; i++) {
        if (color[i] == 0) {
            ans = max(ans, dist[i]);
        }
    }

    cout << ans << endl;

    return 0;
}