牛客 Shortest Path 树形dp

牛客 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);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43860866/article/details/113250045