浅谈树上差分
【引子】
我们遇到一些关于树的问题时,往往需要我们统计一些树上的信息,比如子树和,路径边覆盖、点覆盖(目前没见过别的类型)。暴力的解法当然是遍历逐个点对其权值进行修改。
类比序列问题,其在进行区间修改时,可以用差分将\(O(n)\)复杂度降为\(O(1)\)。在树上我们是对一条链进行处理,那差分在树上可不可用呢?答案是肯定的。
【从序列到树】
在一个序列上进行差分的操作,相信各位都十分熟悉:假设当前我们要对一个序列的\(l\sim r\)区间的每个数执行\(+k\)操作,那么对于差分数组,我们在\(l\)位置\(+k\),表示从\(l\)开始有一个\(+k\)的影响,在\(r+1\)位置\(-k\),表示在\(r+1\)这个位置影响被消除。这个影响是从序列首端传递到序列尾端的。
首先我们要明白,对于一条树上的链,假设其起点为\(s\),终点为\(t\),其一定可以分为两部分:\(s\sim lca(s,t),lca(s,t)\sim t\)。或者说,对于任意一棵树,对于点对\((s,t)\),它所表示的路径是唯一的。
感性的理解一下,由于树上的任意点只存在一个父节点,那么如果这个点不断向父节点移动,路径就是唯一的。那么对于一个点对,这两个点不断向上移动,减少深度的时候,就一定会相遇,我们叫这个相遇的点“最近公共祖先”,也就是LCA。
【树上差分】
那么如果我们将差分技巧拓展到树上会如何呢?差分的核心思想是某种影响的产生与消除。显然,对于一条树链,它的影响产生于\(s\),消除于\(t\)。但是,一棵树上有那么多条链,如果这样进行差分的话,最后如何统计整棵树的点的权值呢?
既然单独对一条条链进行差分无法达到要求,那我们不妨把整棵树作为一个差分的对象。上文提到,任意点的父节点只有一个,也就是说任意点到根节点的路径也是唯一的。如果我们把根节点那边类比做序列尾端,把叶子节点那边类比做序列首端,那这样的话,这个影响不就能自底向上传递了吗?
具体地说,如果我们有差分数组\(c[]\),对于任意一棵树,假设我们要对\(s\sim t\)这条路径上的所有点执行\(+1\)操作,那么我们就在\(c[s]+1\),在\(c[t]+1\),在\(c[lca(s,t)]-1\),在\(c[father(lca(s,t))]-1\)。然后我们自底向上传递信息,意即对于节点\(x\),假设它的子节点集合为\(son\),子树和为\(ans[x]\),那么有\(ans[x]=c[x]+\sum c[i],i\in son\)。这个信息是从叶子节点逐层向上传递的。
对于任意多个点对\((s,t)\)的询问,我们重复上述差分操作,最后进行自底向上的统计即可。
讨论完了点覆盖的情形,我们来考虑边覆盖。点覆盖与边覆盖唯一的区别就在于当前子树的根节点是否计算在内。显然,点覆盖的情况是包含根节点的,而边覆盖是不包含的。因此,我们只需稍稍修改差分操作:在\(c[s]+1\),在\(c[t]+1\),在\(c[lca(s,t)]-2\)。最后进行统计即可。
几道例题
P3128
一道裸题,适合入门树上差分,非常简单。
P2608
一道结合了一点点别的东西的树上差分裸题,需要一点思维量,还算比较简单。
P4556
涉及到线段树合并,建议去学,正常难度的题目。当然如果你会树剖就当我没说。
P1600
啊我死了。
【扯点淡】
最近真是越来越常考树论了。。。特别是树上差分老是考,我的建议是学树剖或者LCT,便于成为调参带湿。其实会树上差分也差不多够用了,多学点也没负面影响。主要是倍增LCA常数感人,好在NOIpCSP似乎并不会卡。
原文地址:https://www.cnblogs.com/DarkValkyrie/p/11625846.html