洛谷

洛谷题单指南-树与图上的动态规划-P2986 [USACO10MAR] Great Cow Gathering G

原题链接:https://www.luogu.com.cn/problem/P2986

题意解读:一棵树中,边权是距离,点权是人数,找到一个点到所有人距离之和最小,求这个最小距离和。

解题思路:

换根DP的典型题,思路可以参考:https://www.cnblogs.com/hackerchef/p/18075408

核心就是两次DFS:

1、第一次DFS,求f[1]:1到所有人距离之和,计算每个点i为根的子树大小sz[i]

2、第二次DFS,对于边u->v,通过f[u]递推f[v],这里面的转换关系根据参考题解中的内容容易得出

100分代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 100005, M = 200005;

int c[N];
int head[N], to[M], w[M], nxt[M], idx;
LL f[N]; //f[i]表示所有牛到i点的距离之和
int sz[N]; //sz[i]表示以i为根的子树大小
int n;

void add(int a, int b, int c)
{
    to[++idx] = b;
    w[idx] = c;
    nxt[idx] = head[a];
    head[a] = idx;
}

void dfs1(int u, int fa, int dist)
{
    f[1] += 1ll * c[u] * dist;
    sz[u] = c[u];
    for(int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if(v == fa) continue;
        dfs1(v, u, dist + w[i]);
        sz[u] += sz[v];
    }
}

void dfs2(int u, int fa, int dist)
{
    for(int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if(v == fa) continue;
        f[v] = f[u] - 1ll * sz[v] * w[i] + 1ll * (sz[1] - sz[v]) * w[i];
        dfs2(v, u, dist + w[i]);
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> c[i];
    for(int i = 1; i < n; i++)
    {
        int a, b, l;
        cin >> a >> b >> l;
        add(a, b, l), add(b, a, l);
    }

    dfs1(1, 0, 0);
    dfs2(1, 0, 0);

    LL ans = LLONG_MAX;
    for(int i = 1; i <= n; i++) ans = min(ans, f[i]);
    cout << ans;
    return 0;
}