很多路径一起统计
树上有很多条路径,每条路径都要加 1、标记或统计覆盖次数,最后才统一查询。
把很多次路径加值,压缩成端点和 LCA 附近的几次加减;最后一次 DFS 从子树往父亲汇总。
树上有很多条路径,每条路径都要加 1、标记或统计覆盖次数,最后才统一查询。
每条路径暴力走可能是 O(n)。树上差分把一次路径操作变成 LCA 查询加四次左右的加减。
八级复习先保点差分:u++ v++ L-- parent[L]--。边差分只需能区分。
一维差分的还原是从左到右加前缀;树上差分的还原是从下到上加子树。
这句话的含义是:child 子树里的路径贡献,如果还没有被 LCA 附近抵消,就必须经过 u 继续往上走。
| 步骤 | 操作 | 作用 |
|---|---|---|
| 1 | diff[3]++ | 端点 3 产生贡献 |
| 2 | diff[4]++ | 端点 4 产生贡献 |
| 3 | diff[2]-- | LCA 保留一份贡献 |
| 4 | diff[1]-- | 阻止贡献传到 LCA 上方 |
| 5 | DFS 汇总 | 子节点还原值加给父亲 |
int L = lca(u, v);
diff[u] += k;
diff[v] += k;
diff[L] -= k;
diff[fa[L][0]] -= k;
int L = lca(u, v);
diff[u] += k;
diff[v] += k;
diff[L] -= 2 * k;
点差分里 LCA 是路径上的点,要保留一份;边差分里 LCA 到父亲的边不在路径上,所以扣两份。
点差分路径 u → v 加 1,正确公式是哪一个?
还原时为什么是子节点加给父节点?
P3128 Max Flow P 更像哪一种?
int diff[N];
vector<int> g[N];
int fa[N][LOG];
void add_point_path(int u, int v, int k) {
int L = lca(u, v);
diff[u] += k;
diff[v] += k;
diff[L] -= k;
diff[fa[L][0]] -= k; // 根的父亲是 0,0 号点不统计
}
void dfs_sum(int u, int p) {
for (int v : g[u]) {
if (v == p) continue;
dfs_sum(v, u);
diff[u] += diff[v]; // 子树贡献汇总到父亲
}
// 此时 diff[u] 就是点 u 的最终贡献
}
读代码时只盯两件事:路径标记用 LCA,最终答案用 DFS 子树和还原。
| 材料 | 定位 | 今天怎么用 |
|---|---|---|
| P3379 | LCA 模板 | 先写稳,树差依赖它 |
| P3128 | 经典点差分 | 练公式和 DFS 汇总 |
| P11967 | GESP202503 八级真题 | 讲评用,理解路径覆盖 |