多条树上路径都给点加 1,用点差分一次性求每个点被经过次数。
P3128 [USACO15DEC] Max Flow P
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
每条运输路线都沿路径逐点加,K 到 1e5 会超时。
二、核心观察
对路径 (u,v) 做 diff[u]++, diff[v]++, diff[lca]--, diff[parent(lca)]--。
为什么不丢答案:DFS 子树和会把两个端点的贡献沿路径向上合并,路径外贡献被抵消。
路径端点
LCA
cnt[u]
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么是点差分?
先判断,再点按钮。
答案在哪里更新?
先判断,再点按钮。
五、手推结果
| 操作 | 数组变化 |
|---|---|
| 路径 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;
}