P1395 会议

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

一、先回答问题

结论

会议地点是树的重心;若有多个,选编号最小,再求距离和。

容易误入的路

选度数最大的点不可靠,树形结构和子树规模才关键。

二、核心观察

建模与压缩

删除点 u 后最大连通块越小,u 越接近全体人的中心。

为什么不丢答案:树的重心最小化所有点到它的距离和;两个重心时距离和相同。

size[u] max_part[u] 距离和

三、互动演示

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

四、自己选答案

问题 1

重心判断看什么?

问题 2

距离和用什么类型?

五、手推结果

含义
sz[u]子树大小
max_part删 u 后最大块
total_dist到重心距离和

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

六、C++14 参考代码

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

const int MAXN = 50005;
vector<int> adj[MAXN];
int sz[MAXN], max_part[MAXN];
int n, centroid = 0, min_max_part = 1e9;
long long total_dist = 0;

// 第一次 DFS:找重心
void find_centroid(int u, int p) {
    sz[u] = 1;
    max_part[u] = 0;
    for (int v : adj[u]) {
        if (v == p) continue;
        find_centroid(v, u);
        sz[u] += sz[v];
        max_part[u] = max(max_part[u], sz[v]);
    }
    max_part[u] = max(max_part[u], n - sz[u]);
    
    // 更新重心:寻找最大连通块最小的点
    if (max_part[u] < min_max_part) {
        min_max_part = max_part[u];
        centroid = u;
    } else if (max_part[u] == min_max_part) {
        centroid = min(centroid, u); // 编号最小
    }
}

// 第二次 DFS:计算距离和
void get_dist(int u, int p, int d) {
    total_dist += d;
    for (int v : adj[u]) {
        if (v != p) get_dist(v, u, d + 1);
    }
}

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

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

    find_centroid(1, 0);
    get_dist(centroid, 0, 0);

    cout << centroid << " " << total_dist << endl;

    return 0;
}