19 ACwing 287 积蓄程度 题解

积蓄程度

题面

给定一个 \(n\) 个点,\(n - 1\) 条边的树形流网络,每条边都有容量

对于每个度数为 1 的点,将其当做源点(根),其余度数为 1 的点当做汇点,求最大流

\(n \le 2\times 10^5\)

题解

这道题不难想到朴素的 \(O(n^2)\) 做法,就是枚举哪个点为根,然后dfs一遍求得答案

但是这样过不了这题,所以就要用到我们的换根法,能够在 \(O(n)\) 的时间复杂度内解决本题

首先我们设 \(f(x)\) 表示以 1 为源点时从 \(x\) 节点能够流出去的最大流量,\(g(x)\) 表示以 \(x\) 为源点能够流出去的最大流量

首先 \(f\) 数组我们可以 \(O(n)\) 做一遍dfs求出,而后要思考的是如何求出 \(g\) 数组

对于 \(1\)\(g(1) = f(1)\)

那么我们利用类似数学归纳法的方式来推导这个dp过程

假设我们已经求出了 \(g(x)\) ,设 \(y \in son_x\)

\[g(y) = f(y) + min \{ e(x, y),\ g(x) - min \{e(x,y),\ f(y)\} \} \]

这样我们就可以在 \(O(n)\) 的时间复杂度内求出结果

注意一些细节

  • 一开始选的根不能是某个叶子节点,否则后面换根的时候某个节点的父节点会成为出水口,不方便统计,所以我们保证一开始选的根为非叶子结点
  • 如果换根时儿子节点是叶子,那么就不加 \(f(y)\)

具体细节看代码

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 2e5 + 10, M = N << 1;
const int INF = (1ll << 31) - 1;

int n;
int h[N], ver[M], ne[M], e[M], tot;
int f[N], g[N], deg[N];
bool leaf[N];

void add (int x, int y, int z) {
    ver[++tot] = y;
    ne[tot] = h[x];
    h[x] = tot;
    e[tot] = z;
}

void dfs1 (int x, int fa) {
    f[x] = 0;
    bool fn = 0;
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == fa) continue;
        fn = 1;
        dfs1 (y, x);
        f[x] += min (f[y], e[i]);
    }
    if (!fn) {
        leaf[x] = 1;
        f[x] = INF;
    }
}

void dfs2 (int x, int fa) {
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == fa) continue;
        if (leaf[y]) {
            g[y] = min (e[i], g[x] - min (e[i], f[y]));
        } else {
            g[y] = min (e[i], g[x] - min (e[i], f[y])) + f[y];
        }
        dfs2 (y, x);
    }
}

void solve () {
    memset (h, 0, sizeof h);
    memset (leaf, 0, sizeof leaf);
    memset (deg, 0, sizeof deg);
    tot = 0;

    cin >> n;
    for (int i = 1; i < n; i ++) {
        int x, y, z;
        cin >> x >> y >> z;
        add (x, y, z);
        add (y, x, z);
        deg[x] ++;
        deg[y] ++;
    }
    int rt = 0;
    for (int i = 1; i <= n; i ++) {
        if (deg[i] > 1) {
            rt = i;
            break;
        }
    }
    if (!rt) {
        cout << e[1] << endl;
        return;
    }
    dfs1 (rt, 0);
    g[rt] = f[rt];
    dfs2 (rt, 0);
    int ans = 0;
    for (int i = 1; i <= n; i ++) {
        ans = max (ans, g[i]);
    }
    cout << ans << endl;
}

int main () {
    int T;
    cin >> T;
    while (T --) {
        solve ();
    }
    return 0;
}
posted @ 2025-10-05 18:01  michaele  阅读(9)  评论(0)    收藏  举报