学习笔记 - 树上差分(边差分)
前言
我是傻逼。
普通的差分很简单,记录相邻两个元素的差值。
当需要还原修改后的数组时,我们只需要把每个数之间的差值挨个累加。
也就是跑一遍前缀和。
即前缀和是差分的逆运算。
边差分
当我们需要为树上的一条路径上的点或边统一加权值时,就会用到树上差分。
其原理和普通差分一样。
修改
在边差分中,一条边的差分值是它和它下方所有边权之和的差。
![]()
如图:
$diff[1,3] = val[1, 3] - val[3, 4]$
考虑一个简单的问题:如果我们将根节点 $1$ 至节点 $u$ 的一条路径上的边统一加权,怎么办?
我们只需要把 $u$ 连到它父亲的边加上这个权值就好了。
那么从一个节点 $u$ 到它的祖先 $v$ 这一段加权呢?
照样把 $u$ 连到它父亲的边加上这个权值,但是 $v$ 连到它父亲的边要减掉这个权值,因为它并没有加权。
将这个问题扩展到任意节点 $u, v$ 之间。
记 $LCA(u, v) = r, fa(r) = f$ 。
可以将这个路径拆成两段,一段是 $(u, r)$,另一段是 $(v, r)$。
两段分别操作就行了。注意 $(f, r)$ 这条边的的差分要减两倍。
参考代码
void add(int u, int v, int val) { int r = lca(u, v); diff[id[make_pair(fa[u][0], u)]] += val; diff[id[make_pair(fa[v][0], v)]] += val; diff[id[make_pair(fa[r][0], r)]] -= val * 2; }
还原
显然,要还原一条边的值,必须要把它下面所有边的值都求出来。
叶子节点连的边的值就是它的差分数组值本身。
直接递归处理即可。
参考代码
void dfs(int u, int v) { sum[id[make_pair(u, v)]] = diff[id[make_pair(u, v)]]; for (auto &i : e[v]) { if (i != u) { dfs(v, i); sum[id[make_pair(u, v)]] += sum[id[make_pair(v, i)]]; } } }
本文作者:aaaaaaqqqqqq
本文链接:https://www.cnblogs.com/aaaaaaqqqqqq/p/17976962
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步