高级数据结构

发布时间 2024-01-08 14:11:35作者: 牛肉爱吃dks

并查集

  • 普通并查集的应用只需要注意边带权的转化。
  • 合并方式优化按深度大小要更合理一些。
  • 可持久化并查集:把原来的 \(fa\) 数组变成可持久化数组,注意不要路径压缩,直接按秩合并即可。
  • 可撤销并查集:还是不要路径压缩,只用按秩合并,每次记录一下修改了什么,撤销的时候倒回去就行。只需要撤销的时候就不需要用可持久化了。
  • 并查集的本质是树(森林),所以有时候可以考虑转化为树上问题解决。
  • Examples

树状数组

  • 树状数组的基础题直接套板子就行了,但是更复杂的应用需要回到树状数组的本质(编号方式)去思考,比如解决不满足线性性的最值问题。
  • 解决像区间修改区间查询这类问题需要一些转化,变为若干可以用树状数组解决的问题。
  • 树状数组可以和倍增思想结合,进行树状数组上二分

线段树

  • 普通线段树不要为了省空间开两倍,不然怎么死的都不知道。开四倍是因为极端情况会刚好多出一个点再最后一行。
  • 考点经常在懒标记和信息维护上,线段树只是糖衣。
  • 吉司机线段树:记录区间的最大值和次大值,两者都要修改时直接递归到儿子。解决区间最值区间修改一类的问题。
  • 线段树非常适合做区间合并,可以合并左右儿子区间对称的信息,也可以合并单向的信息(比如从左往右的)。
  • 找思路别忘了扫描线
  • 如果一道题要用到树套树,那最好还是再想想有没有忽略一些别的信息或者思考一下别的方法,真正要用到树套树的题挺少的(部分分除外)。
  • 线段树上二分比普通二分+线段树查询少一个 \(\log\)

可持久化线段树

  • 经常在把一段区间截出来后搭配线段树上二分
  • 区间修改的主席树其实没有多多少复杂度。可以这样思考:线段树的区间操作是 \(O(\log n)\) 的,主席树的区间修改时的遍历方式其实和线段树的区间操作类似,也就是 \(O(\log n)\) 的,增加的点数也是 \(O(\log n)\) 的,和单点修改没啥差别,只是多了一个标记永久化。

分块与莫队算法

  • 优美的暴力。
  • 考虑一种基于分块思想优化:尽量将每种操作的复杂度平均。例如最简单的, \(O(1)\) 修改,\(O(n)\) 询问,可以尝试变成 \(O(\sqrt n)\) 修改 \(O(\sqrt n)\) 询问。还有不太容易想到的,\(O(\log n)\)\(O(\sqrt n \log n)\) 平均成 \(O(\sqrt n)\) 等。常用值域分块实现。
  • 大分块的考点常在信息维护上。
  • 莫队解决的时序列上的问题,如果是树上问题可以尝试用 DFS 序欧拉序括号序等转化。

块状链表

简单树上问题

  • 树的重心常在点分治点分树中用到
  • 树的直径用两次 DFS 求是最基础的,但是如果加上一些修改就需要从树形 dp 的方向思考了。

LCA

  • 倍增求 LCA 有时会顺带倍增维护一些其他的信息比如求链上最值之类的。
  • tarjan 求 LCA:离线求 LCA,遍历树,到点 \(u\) 时如果 \(v\) 被遍历过,那么 \(lca(u,v)\) 就是 \(v\) 祖先中深度最大的没有被回溯的点。
  • 没有办法离线询问有比较多的情况可以用欧拉序 + RMQ 求 LCA。
  • 理论上来说树剖求 LCA 会比倍增快一点,虽然时间复杂度时一样的。