树上野原式合并
后续:优势是常数小,代码短;缺点是暂无法维护非局部信息。(现在可以维护非局部信息树的重心,不过大概是特例)
有人说,直接点分树,力大砖飞,非常不错!
实际上这种做法非常的垃圾,很多时候我们使用点分树,一定是不得不使用点分树,比如模板题,强制在线,非常的恶心,所以我们使用点分树。
而点分树是否必要呢?既然你能看到这篇blog,他肯定是不必要的!我们今天来发掘dsu on tree的美妙性质,让他求解这个问题!
然后就是我们考虑dsu on tree如果传统从子树向上合并,非常劣,因为这样的结构既我不会可持久化,而且不能够处理子树外信息,非常垃圾。
所以我们考虑自顶向下拆分轻儿子,这个结构非常有趣,首先时间复杂度证明沿用dsu on tree的时间复杂度证明,同时支持可持久化,这个我们先放到一边,最主要的是,他可以维护邻域了。
邻域一直是一个很神秘的东西,看着就跟dsu on tree无缘,但是我们通过这样的方式,他的结构就变得能够维护邻域了,非常的让人开心。
但是如何维护呢?如果每个点都分裂肯定会爆炸,怎么办呢?
考虑我们优先走重链,那么轻儿子因为是挂上去的,所以可以直接合并,我们有距离公式 \(dis(a,b)=dep_a+dep_b-2dep_{lca(a,b)}\),因为这个轻儿子是挂到重链上的,所以 lca 我是确定的,换句话说,我们可以把 \(dep_a-2dep_{lca(a,b)}\) 存到一个数据结构或者干脆就是一个桶里面去,然后用 \(dep_b\) 来查,这部分因为用的是同一个坐标所以合并到同一个数据结构里面去。
然后走轻边的时候,把当前子树信息从主子树信息中删除,然后开一个新的数据结构,旧的保存就好,顺便储存一下旧的版本的 lca(因为不能直接储存dep-2lcadep,直接储存的话时间复杂度是错的),每次需要查询邻域的时候调出来查询就好了,因为到根链轻边只有log条,所以时间复杂度是对的,到这里我们成功 \(\log\) 解决了这个问题,数据结构复杂度另说。
首先大家知道dsu on tree 没办法在线,实际上真的没有办法吗?传统做法我不会,但是这个做法非常容易的我们就能够做到可持久化,一下子就支持在线了,非常的叫人喜欢!!!
相较于点分树,这个算法常数非常小,而且大多数时候跑不满,根据个人需要可以选择线段树或者树状数组维护。
而且最重要的,这个巨好写好调,模板我整理一下就发出来。
大概就是这样,T存储的是目前所有轻边上挂的,Whe是和T对应的,存储其LCA,Light是大数据结构,getans是求解答案的部分,根据情况自行调整。
对于P5666,子树内信息是容易维护的,考虑子树外,我们可以反过来维护。
建立一个虚根,每次点往下走都是在加入别的子树,所以我们优先进入重儿子,再加入轻儿子,因为重心拥有优美性质,加入或者删除一个叶子只会动一下,所以是很好做的。
而我们每次进入轻儿子相当于是在当前情况下删除当前子树所有节点后变成子问题,非常好。