会议地点是树的重心;若有多个,选编号最小,再求距离和。
P1395 会议
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
选度数最大的点不可靠,树形结构和子树规模才关键。
二、核心观察
删除点 u 后最大连通块越小,u 越接近全体人的中心。
为什么不丢答案:树的重心最小化所有点到它的距离和;两个重心时距离和相同。
size[u]
max_part[u]
距离和
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
重心判断看什么?
先判断,再点按钮。
距离和用什么类型?
先判断,再点按钮。
五、手推结果
| 值 | 含义 |
|---|---|
| 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;
}