据说近几年NOIp考得比较多, 于是去研究了一下, 做点笔记
树上前缀和:
说到差分就不得不说到前缀和(因为两个互为逆操作). 对于树上差分, 我们需要定义一个类似前缀和的操作来将差分转化为原来的值
定义树上某个结点的前缀和为它本身及其所有后代结点的权值之和
(为什么这样定义? 也许是好算吧…)
树上差分:
说完树上前缀和, 进入正题
差分一般是用来做区间修改的, 而树上差分也是一个道理
这里树上的”区间”指的其实是一条树链
我们可以对每个树上的结点定义一个数d, 表示某个结点与它所有子结点的权值之和的差, 叶子结点的d即为它本身
定义了d之后, 我们就可以和序列一样, 把树上”区间”的操作转化为单点的操作.
比如上图, 我们如果要把4~8的路径上的所有结点的权值加上1, 那么对于d来说则是$d_8+1,d_1-1$
同理, 如果要把1~7的路径上的所有结点权值加3, 对应d的操作则是$d_7+1,d_0-1$ (假设1的父节点是0)
我们可以记录下d的改变量, 当需要询问某个点改变后的权值时, 可以直接用dfs求该结点的d改变量的前缀和(即它本身权值的改变量)
应用:
一般来说, 涉及树上差分的题目一般是对于每两个点间的简单路径进行修改, 所以做这类题时首先要学会怎么求两点的LCA.
一般这类问题分为两种: 点的修改和边的修改
点的修改:
还是拿上图举例, 假如我们需要将5~6之间的简单路径上的点的权值加上1, 它们的LCA是2, 我们可以把这条简单路径拆分为两条树链: 2~5和2~6
首先对2~5进行操作, 即$d_5+1,d_1-1$;
然后对2~6进行操作, 即$d_6+1,d_1-1$;
然后我们对2结点重复加了一次, 需要减掉一次, 操作即为$d_2-1,d_1+1$;
最终所做的更改为:
由这个例子可以看出, 对于树上的某两个点u,v, 如果要对他们两个之间的简单路径上的所有点权值加k, 则对应d的操作为: