洛谷 P1395 会议 -普及/提高-
P1395 会议
题目描述
有一个村庄居住着 nnn 个村民,有 n−1n-1n−1 条路径使得这 nnn 个村民的家联通,每条路径的长度都为 111。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。
输入格式
第一行,一个数 nnn,表示有 nnn 个村民。
接下来 n−1n-1n−1 行,每行两个数字 aaa 和 bbb,表示村民 aaa 的家和村民 bbb 的家之间存在一条路径。
输出格式
一行输出两个数字 xxx 和 yyy。
xxx 表示村长将会在哪个村民家中举办会议。
yyy 表示距离之和的最小值。
输入输出样例 #1
输入 #1
4
1 2
2 3
3 4
输出 #1
2 4
说明/提示
数据范围
对于 70%70\%70% 数据 n≤103n \le 10^3n≤103。
对于 100%100\%100% 数据 n≤5×104n \le 5 \times 10^4n≤5×104。
solution
- 思路: 其实就是重心,任选一个节点作为 root,第一遍dfs算出各节点子树节点到该节点的距离和,和子树节点个数,第二遍dfs在考虑上从父节点过来的分支距离
- 具体:
- 1 设
f[u]: 以u为根的子树节点距离和,
siz[u]:以u为根的子树节点个数
g[u]: 的所有节点到u的距离和
- 1 设
- 2 递推:v 是 u 的子节点
- dfs(1, 0)
-
siz[u] += siz[v];f[u] += siz[v] + f[v];
- dfs2(1, 0)
-
g[1] = f[1]g[v] = g[u] + (n - 2 * siz[v]);
代码
#include<iostream>
#include<algorithm>
#include "cstring"
#include "vector"using namespace std;/** P1395 会议* 题目大意: 有一颗无根无权树,找到树中一个节点,使得其它所有节点的到该点的距离和最小** 思路: 其实就是重心,任选一个节点作为 root,第一遍dfs算出各节点子树节点到该节点的距离和,和子树节点个数* 第二遍dfs在考虑上从父节点过来的分支距离* 具体:* 1 设 f[u]: 以u为根的子树节点距离和,siz[u]:以u为根的子树节点个数,* g[u]: 的所有节点到u的距离和* 2 递推:v 是 u 的子节点* dfs(1, 0)* siz[u] += siz[v];* f[u] += siz[v] + f[v];* dfs2(1, 0)* g[1] = f[1]* g[v] = g[u] + (n - 2 * siz[v]);**/typedef long long ll;
const int N = 5e4 + 5;int n, siz[N], f[N], g[N], Min, k;
vector<int> e[N];void dfs(int u, int p) {siz[u] = 1;for (int v: e[u]) {if (v == p) continue;dfs(v, u);siz[u] += siz[v];f[u] += siz[v] + f[v];}
}void dfs2(int u, int p) {for (int v: e[u]) {if (v == p) continue;g[v] = g[u] + (n - 2 * siz[v]);if (g[v] < Min || g[v] == Min && v < k) k = v, Min = g[v];dfs2(v, u);}
}int main() {cin >> n;for (int i = 1, x, y; i < n; i++) {cin >> x >> y;e[x].push_back(y);e[y].push_back(x);}dfs(1, 0);Min = f[1], k = 1, g[1] = f[1];dfs2(1, 0);cout << k << ' ' << Min << endl;return 0;
}