《P2052 [NOI2011] 道路修建》
题目描述
在 W 星球上有 n 个国家。为了各自国家的经济发展,他们决定在各个国家之间建设双向道路使得国家之间连通。但是每个国家的国王都很吝啬,他们只愿意修建恰好 n−1 条双向道路。
每条道路的修建都要付出一定的费用,这个费用等于道路长度乘以道路两端 的国家个数之差的绝对值。例如,在下图中,虚线所示道路两端分别有 2 个、4 个国家,如果该道路长度为 1,则费用为 1×∣2−4∣=2。图中圆圈里的数字表示国家的编号。
由于国家的数量十分庞大,道路的建造方案有很多种,同时每种方案的修建费用难以用人工计算,国王们决定找人设计一个软件,对于给定的建造方案,计算出所需要的费用。请你帮助国王们设计一个这样的软件。
输入格式
输入的第一行包含一个整数 n,表示 W 星球上的国家的数量,国家从 1 到 n 编号。
接下来 n−1 行描述道路建设情况,其中第 i 行包含三个整数 ai,bi 和 ci,表示第 i 条双向道路修建在 ai 与 bi 两个国家之间,长度为 ci。
输出格式
输出一个整数,表示修建所有道路所需要的总费用。
输入输出样例
输入 #1复制
6 1 2 1 1 3 1 1 4 2 6 3 1 5 2 1
输出 #1复制
20
说明/提示
对于 100% 的数据,1≤ai,bi≤n,0≤ci≤106,2≤n≤106。
测试点编号 | n= |
---|---|
1 | 2 |
2 | 10 |
3 | 100 |
4 | 200 |
5 | 500 |
6 | 600 |
7 | 800 |
8 | 1000 |
9 | 104 |
10 | 2×104 |
11 | 5×104 |
12 | 6×104 |
13 | 8×104 |
14 | 105 |
15 | 6×105 |
16 | 7×105 |
17 | 8×105 |
18 | 9×105 |
19,20 | 106 |
代码实现:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
struct Edge {
int a, b, c;
};
vector<vector<pair<int, int > > > adj; // 邻接表:adj[u]存储(u的邻接点, 边的长度)
vector<int> size_arr; // 存储每个节点为根的子树大小
vector<int> parent; // 存储每个节点的父节点
vector<bool> visited; // 标记节点是否已访问
// 深度优先搜索计算子树大小
void dfs(int u) {
visited[u] = true;
size_arr[u] = 1; // 子树大小初始化为1(包含自身)
// 遍历u的所有邻接点(使用下标访问)
for (int i = 0; i < adj[u].size(); ++i) {
// 获取邻接点v和对应的边长度(这里边长度暂时用不到)
int v = adj[u][i].first;
// int c = adj[u][i].second; // 暂时不需要
// 如果v不是父节点且未访问过,说明是子节点
if (!visited[v] && v != parent[u]) {
parent[v] = u; // 记录父节点
dfs(v); // 递归计算子树大小
size_arr[u] += size_arr[v]; // 累加子树大小
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
// 初始化容器大小
adj.resize(n + 1);
size_arr.resize(n + 1, 0);
parent.resize(n + 1, -1);
visited.resize(n + 1, false);
vector<Edge> edge_list(n - 1); // 存储所有边的信息
// 读取边信息并构建邻接表
for (int i = 0; i < n - 1; ++i) {
int a, b, c;
cin >> a >> b >> c;
// 向邻接表中添加双向边(用make_pair构造pair)
adj[a].push_back(make_pair(b, c));
adj[b].push_back(make_pair(a, c));
// 保存边的信息
edge_list[i].a = a;
edge_list[i].b = b;
edge_list[i].c = c;
}
// 从节点1开始DFS(任意节点均可作为根,这里选1)
dfs(1);
// 计算总费用
ll total = 0;
for (int i = 0; i < n - 1; ++i) {
int a = edge_list[i].a;
int b = edge_list[i].b;
int c = edge_list[i].c;
// 确定子节点(边的一端是另一端的父节点)
int child;
if (parent[b] == a) {
child = b; // a是b的父节点,子树为b
} else {
child = a; // b是a的父节点,子树为a
}
// 子树大小为size_arr[child],另一端子树大小为n - size_arr[child]
int s = size_arr[child];
int k1= 2*s - n;
total += (ll)c * abs(k1); // 费用 = 长度 * |2s - n|
}
cout << total << endl;
return 0;
}