牛客 Shortest Path
题意
一棵 n ( 保 证 n 是 偶 数 ) n(保证n是偶数) n(保证n是偶数) 个点的树,边带权。要求找出 n 2 \frac{n}{2} 2n 对点,使得每个点对的距离之和最小。
题解
- 首先一条边不会被覆盖两次,如果覆盖了两次,一定有更优的组合方案;
- 对于一个点 u u u ,一定是优先子树内部消耗,否则 u u u 和 u u u 的父亲节点的边会被多次计算;
- 如果点 u u u 的子树大小是偶数,那么一定会内部消耗,否则 u u u 需要连向 u u u 的父亲节点。
代码
#pragma region
//#pragma optimize("Ofast")
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 2e5 + 5;
vector<pair<int, int>> g[maxn];
int n, sz[maxn];
ll ans;
void dfs(int u, int f, int len) {
sz[u] = 1;
for (auto e : g[u]) {
int v = e.first, w = e.second;
if (v == f) continue;
dfs(v, u, w);
sz[u] += sz[v];
}
if (sz[u] & 1) ans += len;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
ans = 0;
rep(i, 1, n) g[i].resize(0);
rep(i, 1, n - 1) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back({
v, w});
g[v].push_back({
u, w});
}
dfs(1, 0, 0);
printf("%lld\n", ans);
}
}