GESP8 · LCA · 路径贡献

树上差分

把很多次路径加值,压缩成端点和 LCA 附近的几次加减;最后一次 DFS 从子树往父亲汇总。

一、先回答问题

什么时候用

很多路径一起统计

树上有很多条路径,每条路径都要加 1、标记或统计覆盖次数,最后才统一查询。

优化掉什么

不再沿路径暴力走

每条路径暴力走可能是 O(n)。树上差分把一次路径操作变成 LCA 查询加四次左右的加减。

考前范围

点差分优先

八级复习先保点差分:u++ v++ L-- parent[L]--。边差分只需能区分。

二、核心模型

一维差分的还原是从左到右加前缀;树上差分的还原是从下到上加子树。

diff[u] += diff[child]

这句话的含义是:child 子树里的路径贡献,如果还没有被 LCA 附近抵消,就必须经过 u 继续往上走。

三、互动演示:路径 3 到 4 的点差分

1 2 3 4 LCA = 2
diff[1]0
diff[2]0
diff[3]0
diff[4]0
目标:路径 3 → 4 上的点 3、2、4 加 1,点 1 不加。
步骤操作作用
1diff[3]++端点 3 产生贡献
2diff[4]++端点 4 产生贡献
3diff[2]--LCA 保留一份贡献
4diff[1]--阻止贡献传到 LCA 上方
5DFS 汇总子节点还原值加给父亲

四、两种公式

点差分

路径上的点加 k

int L = lca(u, v);
diff[u] += k;
diff[v] += k;
diff[L] -= k;
diff[fa[L][0]] -= k;
边差分

路径上的边加 k

int L = lca(u, v);
diff[u] += k;
diff[v] += k;
diff[L] -= 2 * k;
区别

LCA 要不要保留

点差分里 LCA 是路径上的点,要保留一份;边差分里 LCA 到父亲的边不在路径上,所以扣两份。

五、自己选答案

点差分路径 u → v 加 1,正确公式是哪一个?

还原时为什么是子节点加给父节点?

P3128 Max Flow P 更像哪一种?

六、C++14 核心代码

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 子树和还原。

七、GESP8 复习定位

材料定位今天怎么用
P3379LCA 模板先写稳,树差依赖它
P3128经典点差分练公式和 DFS 汇总
P11967GESP202503 八级真题讲评用,理解路径覆盖