打打打打打卡

一道很好的树形dp问题

P10974 Accumulation Degree

题目简述

给定一棵树,每条边有一个正容量。度为 1 的节点称为终端节点(叶子节点)。

定义:

  • A(x):从节点 x 出发,能够流向其他所有终端节点的最大流量之和。
  • 累积度:所有节点中最大的 A(x) 值。

任务是求给定树的累积度。

核心思想

对于任意节点 u,它向叶子节点输送的流量可以分为两个方向:

  1. 向子树方向(不经过父节点)
  2. 向父节点方向(经过父节点)

因此:

text

A(u) = d[u] + up[u]

其中:

  • d[u]:u 向子树方向(子节点方向)能输送的最大流量
  • up[u]:u 通过父节点方向能获得的最大流量

算法步骤

第一步:第一次 DFS(自底向上)

计算每个节点的 d[u](向下流量)。

递推公式

对于 u 的每个子节点 v:

  • 如果 v 是叶子节点(deg[v] == 1):

    text

    d[u] += w(u, v)
    
  • 如果 v 不是叶子节点:

    text

    d[u] += min(d[v], w(u, v))
    

为什么取 min?
从 u 流向 v 方向的流量,受两个因素限制:

  1. (u, v) 的容量
  2. v 向下输送的能力 d[v]
    所以取两者最小值。

叶子节点处理
叶子节点没有子节点,所以在 DFS 时跳过递归,直接累加边容量。


第二步:第二次 DFS(换根,自顶向下)

利用父节点的累积度,计算子节点的累积度。

核心公式

已知父节点 u 的累积度 f[u],要计算子节点 v 的累积度 f[v]

text

flow_to_v = min(d[v], w(u, v))   // u 流向 v 的流量
other = f[u] - flow_to_v          // u 除 v 方向外的其他流量
up_from_parent = min(other, w(u, v))  // v 通过父节点方向获得的流量
f[v] = d[v] + up_from_parent

特殊情况
当 u 是叶子节点时(deg[u] == 1),u 没有其他方向可以提供流量,所以:

text

f[v] = d[v] + w(u, v)

根节点
根节点没有父节点,所以:

text

f[root] = d[root]

第三步:求答案

遍历所有节点,取 f[i] 的最大值。

注意:当 n == 1 时,没有叶子节点(度为1的节点不存在),此时累积度为 0。


时间复杂度

  • 两次 DFS 各遍历所有节点一次:O(n)
  • 总时间复杂度:O(n)
  • 空间复杂度:O(n)

代码实现

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

const int N = 2e5 + 10;

int T, n;
vector<pair<int, int>> g[N];
int d[N];      // d[u]:向下流量
int f[N];      // f[u]:累积度 A(u)
int deg[N];    // 度数

// 第一次DFS:计算向下流量 d[u]
void dfs1(int u, int fa) {
    d[u] = 0;
    for (auto [v, w] : g[u]) {
        if (v == fa) continue;
        
        if (deg[v] == 1) {
            // v是叶子节点,不递归
            d[u] += w;
        } else {
            // v不是叶子节点,先递归再计算
            dfs1(v, u);
            d[u] += min(d[v], w);
        }
    }
}

// 第二次DFS:换根计算累积度 f[u]
void dfs2(int u, int fa) {
    for (auto [v, w] : g[u]) {
        if (v == fa) continue;
        
        if (deg[u] == 1) {
            // u是叶子节点,只能提供w的流量
            f[v] = d[v] + w;
        } else {
            int flow_to_v = min(d[v], w);
            int other = f[u] - flow_to_v;
            f[v] = d[v] + min(other, w);
        }
        
        dfs2(v, u);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> T;
    while (T--) {
        cin >> n;
        
        // 初始化
        for (int i = 1; i <= n; i++) {
            g[i].clear();
            d[i] = f[i] = deg[i] = 0;
        }
        
        // 读入边
        for (int i = 1; i < n; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            g[u].push_back({v, w});
            g[v].push_back({u, w});
            deg[u]++;
            deg[v]++;
        }
        
        // 特殊情况:只有一个节点
        if (n == 1) {
            cout << 0 << '\n';
            continue;
        }
        
        int root = 1;
        
        // 第一次DFS
        dfs1(root, -1);
        
        // 根节点的累积度 = 向下流量
        f[root] = d[root];
        
        // 第二次DFS(换根)
        dfs2(root, -1);
        
        // 求最大值
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = max(ans, f[i]);
        }
        cout << ans << '\n';
    }
    
    return 0;
}
posted @ 2026-07-17 22:04  happy0x3F  阅读(2)  评论(0)    收藏  举报