鱼香rose'Blog

洛谷P2986 [USACO10MAR] Great Cow Gathering G(树形dp)

\(\Huge{洛谷P2986\ [USACO10MAR]\ Great\ Cow\ Gathering\ G}\)

本题有一道简单版本,难度属于树形dp的简单题。

题目地址:P3478 [POI2008] STA-Station - 洛谷

题解:[POI2008] STA-Station/洛谷P3478(树形dp)-CSDN博客

题意

\(n\)个农场,每个农场相互可以到达(联通),有\(n-1\)条路连接这\(n\)个农场。每条路对应的有其长度\(L_i\),然后每个农场有若干头牛\(C_i\)

现在要选择一个农场,并让所有牛都去,求再哪个农场举办能使得所有牛移动距离最小?输出最小的移动距离总和。

数据范围:

  • \(1\leq n\leq 10^5\)
  • \(1\leq A_i\leq B_i\leq n\)
  • \(0 \leq C_i,L_i \leq 10^3\)

思路

对于这道题,如果写过上面那道简单题的话,这道题很容易就能写出来。不过这一道也属于树形\(dp\)中的典中典。

这两道题都属于树形\(dp\)中的选择节点型。

对于这道题,假设x节点最优,那么x节点的最小移动距离总和=所有子节点的移动距离总和+每个子节点的子节点奶牛个数*这个子节点距离x的距离。

上面的描述可以用下列公式表示为:

\[b[x].second=\sum{b[i].second+b[i].first\times z} \]

以上涉及到的每个节点的子孙节点个数以及移动距离总和,都可以通过一次\(dfs\)完成。

然后考虑从\(x\)节点转移到其子节点\(i\)的状态转移方程:

\[b[i].second = b[x].second + ((b[1].first - b[i].first) \times z) - b[i].first \times z \]

第二个dfs中\(b[1].first\)相当于所有奶牛的个数。

标程

#define int long long 
#define fi first 
#define se second
const int N = 1e5 + 10;

int n, res;
vector<PII> a[N];
vector<PII> b(N);

void dfs1(int x, int y) {
    for(auto [i, z] : a[x]) {
        if(i == y) continue;
        dfs1(i, x);
        b[x].fi += b[i].fi;
        b[x].se += b[i].fi * z + b[i].se;
    }
}

void dfs2(int x, int y) {
    for(auto [i, z] : a[x]) {
        if(i == y) continue;
        b[i].se = b[x].se + ((b[1].fi - b[i].fi) * z) - b[i].fi * z;
        dfs2(i, x);
    }
}

void Solved() {
    cin >> n;
    for(int i = 1; i <= n; i ++ ) {
        cin >> b[i].fi;
    }
    for(int i = 1; i < n; i ++ ) {
        int x, y, z; cin >> x >> y >> z;
        a[x].push_back({y, z}); a[y].push_back({x, z});
    }
    dfs1(1, 1);
    dfs2(1, 1);

    res = 1e18;
    for(int i = 1; i <= n; i ++ ) {
        res = min(res, b[i].se);
    }
    cout << res << endl;
}
posted @ 2026-01-15 21:53  鱼香_rose  阅读(0)  评论(0)    收藏  举报