P3128 [USACO15DEC] Max Flow P

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

多条树上路径都给点加 1,用点差分一次性求每个点被经过次数。

容易误入的路

每条运输路线都沿路径逐点加,K 到 1e5 会超时。

二、核心观察

建模与压缩

对路径 (u,v) 做 diff[u]++, diff[v]++, diff[lca]--, diff[parent(lca)]--。

为什么不丢答案:DFS 子树和会把两个端点的贡献沿路径向上合并,路径外贡献被抵消。

路径端点 LCA cnt[u]

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

为什么是点差分?

问题 2

答案在哪里更新?

五、手推结果

操作数组变化
路径 u-v+u +v -lca -parent(lca)
DFS孩子贡献加回父亲
输出最大点权

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

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

const int MAXN = 50005;
vector<int> adj[MAXN];
int fa[MAXN][20], depth[MAXN], diff[MAXN];
int n, k, ans = 0;

// 预处理深度和倍增祖先
void dfs_lca(int u, int p, int d) {
    depth[u] = d;
    fa[u][0] = p;
    for (int i = 1; i < 20; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for (int v : adj[u]) {
        if (v != p) dfs_lca(v, u, d + 1);
    }
}

// 倍增法求 LCA
int get_lca(int u, int v) {
    if (depth[u] < depth[v]) swap(u, v);
    for (int i = 19; i >= 0; i--) {
        if (depth[u] - (1 << i) >= depth[v]) {
            u = fa[u][i];
        }
    }
    if (u == v) return u;
    for (int i = 19; i >= 0; i--) {
        if (fa[u][i] != fa[v][i]) {
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[u][0];
}

// DFS 求差分数组的前缀和(子树和)
void dfs_sum(int u, int p) {
    for (int v : adj[u]) {
        if (v != p) {
            dfs_sum(v, u);
            diff[u] += diff[v];
        }
    }
    ans = max(ans, diff[u]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> k;
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    dfs_lca(1, 0, 1);

    for (int i = 0; i < k; i++) {
        int u, v;
        cin >> u >> v;
        int p = get_lca(u, v);
        diff[u]++;
        diff[v]++;
        diff[p]--;
        diff[fa[p][0]]--;
    }

    dfs_sum(1, 0);

    cout << ans << endl;

    return 0;
}