可持久化算法
最后更新于:2022年6月25日 下午
可持久化数据结构 (Persistent data structure) 总是可以保留每一个历史版本,并且支持操作的不可变特性 (immutable)。(OI-wiki 原话),然而实际上我们常见的可持久化数据结构都是依赖于可持续化线段树。
学习资料:知乎 Pecco 的文章 和 OI-wiki
可持续化线段树(入门)
(单点更新)可持续化线段树,区间修改版本也类似(同样需要打 tag 延迟更新,没有找到例题就不给了)
本质: 每一次建一个新的线段树,但是由于新的线段树和旧的线段树最多只有一条链不一致,因此我们可以共用很多节点。因此我们必须 动态开点 的方式建线段树,并且可以做到真正的在线。
模板例题:https://www.luogu.com.cn/problem/P3919 的解答
权值线段树(根据权值建的可持续化线段树)
权值线段树可以不离散化!!!可以动态开点,用多少使用开多少,离散化只是节省了空间。
权值线段树求静态区间 MEX
静态区间 MEX 可用删除版本的回滚莫队 \(O(n \sqrt{n})\) 解决。
我们对每一个右端点建一颗线段树,注意到我们关心的是每一个值出现的最后的位置。因此我们在线段树上存每个节点的右位置,然后区间存的是区间值的最小值。这样我们就可以利用线段树自带的二分得到 MEX。
注意到此时不需要离散化(即真正的在线),因为根据 MEX 的性质,如果值大于 n,必然不能为 MEX 做贡献。
例题:https://www.luogu.com.cn/problem/P4137 的解答
如果支持修改怎么做呢?
可持久化线段树求静态区间第 k 小(主席树)
此方法由 黄嘉泰 发明,因为缩写 hjt 和当时主席名字缩写一致因此被戏称为 主席树。
首先离散化,不相同的数的个数为线段树的总区间大小 \(m\),然后每个点存的值为当前每个数出现的次数。那么就可以对于区间 $[l, r] $ 的第 \(k\) 小,我们判断第 k 小的数就是找 第 r 颗线段树 \([0, x]\) 的和,减去第 l 颗线段树 \([0, x]\) 的和大于等于 k 的最小的 \(x\)。因为线段树自带二分,因此查询是 \(\log m\) 的。
模板例题:https://www.luogu.com.cn/problem/P3834 的解答
小插曲:原始提交:https://www.luogu.com.cn/record/50179184 中 tree[q] = tree[p];
移动到 update 的开头即可。但是一直没注意到,然后在 codeforces 群经过群友热心讨论,最后被 badcw 找到了问题。
可持久化线段树求动态区间第 k 小(这里动态表示内容可修改)
动态区间第 k 小可以用树状数组维护前缀和,这样单次修改就只需修改 \(O(log)\) 个 线段树。从而在 \(O(n \log^2 n)\) 时空复杂度解决。注意到此时我们此时每颗线段树都只需保存自己最后的版本,因此不用多开点来修改从而节省内存。
例题:https://www.luogu.com.cn/problem/P2617 的解答。
此问题可以 整体二分 解决。
可持久化 Trie
待补