学习笔记 - 树上差分(边差分)

前言

我是傻逼。

普通的差分很简单,记录相邻两个元素的差值。

当需要还原修改后的数组时,我们只需要把每个数之间的差值挨个累加。

也就是跑一遍前缀和

前缀和是差分的逆运算

边差分

当我们需要为树上的一条路径上的点或边统一加权值时,就会用到树上差分。

其原理和普通差分一样。

修改

在边差分中,一条边的差分值是它和它下方所有边权之和的差。

![]()

如图:

$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 中国大陆许可协议进行许可。

posted @   aaaaaaqqqqqq  阅读(53)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.