树上差分

  • Post author:
  • Post category:其他




树上差分建立在差分数组的基础上,所以还不会差分数组的大佬可以先预习一下这篇博客,期望阅读时间

5

分钟:



差分数组








引入这样一个例题,给定一棵

n(n≤10

5

)

个点的树,

m(m≤10

5

)

次操作,将这棵树上的两点之间的最短路径上的每一个点都加

k

或者都减

k

,在这

m

次操作之后求出每个点的值。



首先,在你没有学过树上差分的时候,你会想到用倍增或者是塔尖求出这两个点的

LCA

然后暴力更改,显然这样每一次操作时间复杂度最劣会是

O(n)

,显然不能接受。





那么我们现在引入树上差分。





树上差分我们可以先感性的理解为树上的差分数组。



树上差分分为两种,一种是边的差分,另一种是点的差分。



那么今天我们就先来讲一下点的差分。



大家现在一定会想像差分数组从前往后扫一样从根往下扫,每个点存它与它的父亲的差,但是认真想一下,这样会存在一个问题,如果我将一条路径上每个点的值都增加k,那么这条路径上的其它枝杈都要打标记减去k。



如图:






这样当路径上的枝杈过多时,时间复杂度会非常不优。


那么应该怎么办呢?我们思考一下刚开始学

OI

的时候学到的递归思想来解决这个问题。


那么现在我们定义一个节点

u

的差分数组

B[u]

表示它与它所有儿子的加和的差,那么问题就变得简单多,我们只需要在最后统计答案的时候递归处理即可。


那么相比很多人有些疑惑,我们应该怎么做修改呢?


还是那个问题,现在我们要将两个点的最短路径上的点都加

k

或者减

k

,暂且设这两个点为

u



v



那么我们还需要再定义两个点,

u



v



LCA



lca

(倍增或塔尖求出),

lca

的直接父亲为

father



那么我们在更改的时候只需要做如下操作:



B[u]+=k;


B[v]+=k;


B[lca]-=k;


B[father]-=k;


那么为什么这么做是正确的呢?


我们可以认为我们在给

u

节点的差分数组

+k

的时候也就相当与将

u

到根的所有节点都加了

k

,给

v

节点的修改同理。


那么我们需要将多更改的部分减去,多更改的部分也就是

lca

多更改了一次,从

father

到跟节点多更改了两次,所以我们在将

B[lca]-=k,B[father]-=k

同时就相当于是做了这个操作(

B[lca]-=k

相当与减掉了

lca

多更改的那一次和

father

到跟节点多更改的一次,

B[father]-=k

相当于是剪掉了

father

到跟节点多更改的剩下的那一次,叠加即为减去了多更改的部分)。


当所有的操作都完成后只需要

DFS

递归统计答案即可。



如果你觉得你已经学会了树上差分,那么可以跳转这道例题(博客还没有写出来额)



rp++

转载于:https://www.cnblogs.com/wzc521/p/11032383.html