【题目来源】
【题目描述】
Farmer John 在他的谷仓中安装了 N-1 条管道,用于在 N 个牛棚之间运输牛奶(2≤N≤50000),牛棚方便地编号为 1...N。每条管道连接一对牛棚,所有牛棚通过这些管道相互连接。
FJ 正在 K 对牛棚之间泵送牛奶(1≤K≤100000)。对于第 i 对牛棚,你被告知两个牛棚 si 和 ti,这是牛奶以单位速率泵送的路径的端点。FJ 担心某些牛棚可能会因为过多的牛奶通过它们而不堪重负,因为一个牛棚可能会作为许多泵送路径的中转站。请帮助他确定通过任何一个牛棚的最大牛奶量。如果牛奶沿着从 si 到 ti 的路径泵送,那么它将被计入端点牛棚 si 和 ti,以及它们之间路径上的所有牛棚。
【输入格式】
输入的第一行包含 N 和 K。
接下来的 N-1 行每行包含两个整数 x 和 y(x≠y),描述连接牛棚 x 和 y 的管道。
接下来的 K 行每行包含两个整数 s 和 t,描述牛奶泵送路径的端点牛棚。
【输出格式】
输出一个整数,表示通过谷仓中任何一个牛棚的最大牛奶量。
【输入样例】
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4
【输出样例】
9
【数据范围】
2≤N≤5×10^4,1≤K≤10^5。
【算法分析】
● 树上差分
树上差分是一种在树上高效处理路径修改和查询的算法技巧,核心思想是将路径操作转化为对节点差分数组的单点修改,最后通过一次遍历还原出结果。 它特别适合处理多次对树上路径进行加减操作,最后查询某个点或边的权值这类问题。
(一)核心思想
点差分:对路径 (x, y) 上所有点的权值进行修改。通过在 x 和 y 处 +val,在 lca(x,y) 和其父节点处 -val,最后通过 DFS 自底向上求和即可还原路径上的权值。具体操作为:对路径 (x, y) 加 val,执行 diff[x] += val, diff[y] += val, diff[lca] -= val, diff[fa[lca]] -= val。
边差分:对路径 (x, y) 上所有边的权值进行修改。通常将边权下放给深度较大的子节点,转化为点权问题,处理方式与点差分类似。具体操作为:对路径 (x, y) 加 val,执行 diff[x] += val, diff[y] += val, diff[lca] -= 2 * val(假设边权下放给子节点)。
(二)适用场景
点差分:路径点权修改、子树点权修改、查询点权。
边差分:路径边权修改、查询边权。
(三)与其他算法对比
与线段树对比:树上差分代码简洁,适合离线操作;线段树支持在线查询,但常数较大。
与树链剖分对比:树上差分处理路径修改更高效;树链剖分功能更强大,支持复杂路径查询。
● LCA ← 树上差分常用 LCA
(1)暴力法(向上标记法):https://blog.csdn.net/hnjzsyjyj/article/details/152026341
(2)暴力法(同步前进法):https://blog.csdn.net/hnjzsyjyj/article/details/152070927
(3)倍增法(DFS预处理):https://blog.csdn.net/hnjzsyjyj/article/details/152203103
(4)倍增法(BFS预处理):https://blog.csdn.net/hnjzsyjyj/article/details/152234376
【算法代码】
本代码中的 dfs1 + getLCA 是倍增法求 LCA 的代码。详见:
之后,dfs2 通过后序遍历完成树形差分的合并,将差分标记转化为每条边的实际覆盖次数。也就是说,dfs2 解决 “次数怎么算”的问题。
【参考文献】
https://www.bilibili.com/video/BV1bg4y127QH
https://www.cnblogs.com/Chase-s/p/10410265.html
https://www.cnblogs.com/hetailang/p/16216504.html
https://blog.51cto.com/u_13536312/5371363