数据结构

数据结构

本文整理的部分还包括常见的数据结构处理技巧。

不公开的题会放在:problem 里,来源可能是联考、讲课之类不方便放出来的没有 source 的题目。

树状数组

非常好用,能写树状数组就别写线段树。

有树状数组上二分,原理是节点 \(i\) 表示区间 \((i-\operatorname{lowbit}(i),i]\) 的信息。

线段树与平衡树

平衡树

写好写的 fhq-treap,笔者现在写不出 splay 了。

不可替代的作用是区间翻转操作。

可持久化平衡树

时空常数巨大,谨慎使用。不可替代的作用是处理区间交换操作。

维护分治信息

最常见的就是 \(\min,\max,\sum\)。最大子段和、楼房重建也是经典的分治信息。

一个有趣的复杂例子是 [Ynoi2013] 文化课

查询区间变换

这个问题通用的做法是考虑建线段树,将区间查询拆成 \(\log n\) 个线段树节点,每次喂节点信息,将返回的信息喂下一个节点即可。

这样的问题难点主要在于预处理数据结构查询每个节点的信息。

一个较为简单的例子是 【1】。

可以考虑从左往右/从右往左扫描线,一个例子是 TEST_100

颜色段均摊

即一般提到的 ODT,珂朵莉树。

区间排序的性质和区间推平很像,也可以用颜色段均摊的方法维护。

颜色段一起动

修改会导致颜色段有局部移动。我们需要设计一个可以合并的移动颜色段的标记。
特殊处理颜色段长度为 0 的情形,在平衡树中找到并删除即可。

一个不简单的例子是 [Ynoi2014] 人人本着正义之名

另一个复杂的例子是 「2021 营员交流」大毒瘤

单侧递归问题

典中典是《楼房重建》。这启发我们这样的前后缀极值查询可以使用单侧递归完成。
另一个例子是维护区间匹配剩下的括号的分治信息:[Ynoi2008] rupq

吉老师线段树

均摊线段树应用不少,一般的思路都是将势能视作线段树子树内什么信息是统一的。
子树内颜色个数,子树内相同的 bit 集合,都是类似的思想。

历史和线段树笔者喜欢的理解是线性变换,实现时可以讨论一下哪些位置可以不维护,减小常数。

李超线段树

支持区间插入函数,查询 \(x\) 处函数极值,不局限于一次函数但是也没碰到过别的。
思想是维护区间优势线段,将其他线段往子树区间插入。

线段树合并分裂

合并和分裂同时存在没有复杂度保证。
分裂复杂度是严格单次 \(O(\log n)\) 的,合并分裂一起只需要分析合并的势能,复杂度正确。

分治

CDQ 分治

降维利器,偏序问题里每套一个 cdq 分治问题降一维,多维偏序需要记得有 bitset 做法,超过 4 维就不要用 cdq 了。

如果我们能将修改对查询的贡献拆开我们也可以使用 cdq 分治。

线段树分治

线段树分治可以将带修改的问题离线处理成插入元素,撤销元素的问题。
线段树分治可以认为是半在线的,递归到叶子 \(i\) 经过一定处理再加入左端点 \(> i\) 的信息也是可行的。

猫树分治

猫树分治的优势在于合并是 \(O(1)\) 次的,优于线段树的 \(O(\log n)\),缺点是支持修改较麻烦。

扫描线

任何范围修改查询能差分都差分。
扫描线注意选取的方向和维度,分析一下不同的选择是否等价。

一个重要的方法是在二维平面考虑问题。

对于一个静态的二维问题,我们通过扫描线将其转化为一个动态的一维问题。
即使用扫描线扫一维,数据结构维护另一维。

另一个角度是站在序列上,对于从左往右扫右端点的扫描线,我们维护一个数据结构,支持查询对于当前的 \(r\)\([l,r]\) 的信息是什么。即对右端点扫描线,维护所有左端点答案。

扫描线大部分情况都伴随着差分,需要注意的是不能差分的情况我们可以使用分治。

扫序列维维护时间:序列

静态区间查询

一个有趣的例子是 【2】。

全局子区间查询

两个有趣的题目,都运用了众数的一个套路:Frequency Problem[Code+#1]Yazid 的新生舞会

区间子区间问题

仍然扫描线,变化是问题往往变成维护历史信息。
我们可以维护一个新的标记表示计入一次历史信息。(等价于一种简化后的线性变换。)

一个例子是 Good Subsegments。另一个例子是 Prof. Pang's sequence

分块

一般分块都用于动态的问题(带修),不带修的静态分块除非强制在线,功能被莫队偏序。

一般来说,一个问题只能做根号,可以证明问题不弱于 \(\sqrt n\times \sqrt n\) 的矩阵乘法。

莫队

一般的莫队就是一个二维扫描线。

使用莫队后处理掉两维,剩下的维度使用数据结构维护。
扫描线扫掉一维剩下的都需要用数据结构维护。

回滚莫队

莫队一个经典的降低复杂度的技巧。

经典的例子是维护前驱后继,支持插入删除是 \(O(\log n)\) 或者 \(O(\log_w n),O(\log\log n)\)的数据结构,但只支持删除和撤销是 \(O(1)\) 的链表就可以完成的工作。即:[WC2022] 秃子酋长

二离莫队

二离莫队的本质是将莫队看作查询 \(O(n\sqrt m)\) 次区间的某种信息,如果信息有可减性,我们差分成前缀信息查询。

那么插入 \(O(n\sqrt m)\) 次,查询 \(O(n\sqrt m)\) 次区间信息的莫队,我们二次离线后扫描线变成插入 \(O(n)\) 次,查询 \(O(n\sqrt m)\) 次前缀信息。

如果我们可以平衡插入和查询的代价,我们就可以优化复杂度。

posted @ 2023-07-07 18:45  juju527  阅读(235)  评论(2编辑  收藏  举报