Dsu on tree old
概述ψ(`∇´)ψ
全称树上启发式合并,也叫优雅的树上暴力。
这东西听说实际上是一种静态链分治。
但是无所谓,能拿来做题就行。
启发式算法的思想就是,用你的脑子和直觉去优化一些算法过程。
一个比较常见的例子就是并查集的按秩合并,也算是一种启发式合并:
1 2 3 4 5 6 7 8 |
|
这里把小的集合的接到大的集合下面,原因是集合的大小可以近似的看作集合的高度。
把高度矮的合并到高度高的显然能更快的进行找集合父亲的操作 root()
。
树上启发式合并其实和这个比较类似,也是利用启发式算法的思想,来优化树上子节点信息的合并过程。
这可能也是这个算法叫 dsu on tree 的原因。
应用ψ(`∇´)ψ
泛化ψ(`∇´)ψ
dsu on tree 主要运用于一类树上问题,这类问题一般需要通过儿子子树的信息“合并”来得到父亲子树的信息。
这里的合并可以简单的理解为:“把所有儿子对应的答案直接通过某种方式揉到一起”
如果是在线,且每个节点维护的信息是一个值域或者序列时,可以考虑使用线段树合并。
如果是离线,就可以考虑使用 dsu on tree 来对暴力合并进行优化
在多数时候速度能吊打树上莫队,树套树等难写的算法。
例题ψ(`∇´)ψ
Last mile of the way
给定你一棵树,每一个节点有一个权值 \(w\) 和一个体积 \(a\)。
有 \(q\) 次询问,每次询问形如 \(x, s\),
表示询问在以 \(x\) 为根的子树中,选不超过 \(s\) 的节点,权值和的最大值。
这里没有任何依赖关系,相当于把这个子树里的所有节点提出来当成一个序列来取。
\(1\le n \le 5\times 10^3, q\le 10^5, w_i \le 10^6, a,x,s \le 5\times 10^3\)。
首先这题可以考虑直接用 dfs 序的性质,把每一个子树直接化成一个序列上的区间。
然后就可以直接莫队,但感觉复杂度不太行(本质上是树上莫队)。
有没有更好的算法?
先考虑一个朴素的 \(30pts\) 算法:本题相当于一个去掉依赖限制的树上背包。
所以我们可以仿照树上背包的方程:设 \(dp_{u, i}\) 表示以 \(u\) 为根的子树中,选不超过 \(i\) 的空间的所有方案,属性为权值和最大值。
然后可以对集合进行一个划分:一半是选 \(u\),一半是不选 \(u\)。
考虑对这两个部分各自转移,但实际上除了选/不选 \(u\) 的决策以外,他们的决策转移方式是相同的。
发现转移只需要枚举分配给 \(u\) 所有的儿子以及以它们为根的子树的空间 \(j\),
然后对于每个儿子枚举一个 \(k_v\),表示这个儿子 \(v\) 以及它的子树分到的空间。
对于每个 \(j\),合法的转移状态是一组满足 \(\sum_v k_v = j\) 的 \(k\)。
这个 \(\sum_v k_v = j\) 怎么满足呢? 其实我们只需要关心当前扫描到的儿子分配了多少空间即可,因为我们并不关心当前答案是怎么来的。
所以两重循环就可以了。
还需要记得考虑 \(u\) 选或者不选,因为这里没有强制选 \(u\) 了,所以和有依赖的背包不太一样。
可以写出一个暴力代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
我们仔细分析一下这个过程。
可以发现,在转移一组合法的 \(j\) 的时候,我们实际上是把满足 \(\sum_v k_v = j\) 的一组 \(k\) 合并起来得到 \(dp_{u,j}\) 这个状态。
也就是把所有儿子的状态全部暴力合并起来,所以此时我们可以考虑一个优化。
类似树链剖分,设节点 \(u\) 的重儿子 \(hson_u\) 为 \(siz_v\) 最大的一个儿子 \(v\)。
转移的时候我们直接把重儿子的 \(dp\) 值序列 \(dp_{hson_u}\) 拿过来用。
也就是让 \(u\) 直接继承重儿子的答案。
然后我们暴力把所有轻儿子的子树的答案直接合并到 \(u\) 上,最后再把考虑 \(u\) 的答案算上。
实际上这就是把 \(u\) 自己当作一个单独的轻儿子节点然后进行暴力合并。
所以合并 \(u\) 的答案和合并轻儿子答案的方式应当是一样的。
我们都直接暴力决策对应的节点选或者不选,只是轻儿子需要递归下去继续暴力取。
可以证明这样做之后,对于每个点的复杂度是 \(\text{O}(n \log n)\) 的。
也就是通过启发式合并的思想把 \(\text{O}(n^2)\) 暴力合并优化到了 \(\text{O}(n \log n)\)。
具体实现看代码:
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
复杂度证明可以看看 OI-wiki,之后再补。
总结ψ(`∇´)ψ
总结一下,dsu on tree 其实就是对于需要合并信息的一类离线树上问题,利用启发式合并的思想优化。
过程大致如下:
- 直接让 \(u\) 的信息数组继承 \(hson_u\) 的信息数组。
- 暴力把其它轻儿子的信息合并到 \(u\) 的信息数组上。 这里合并的方式因题而异。
- 最后把考虑 \(u\) 的情况合并上去即可。