数据结构
数据结构
本文整理的部分还包括常见的数据结构处理技巧。
不公开的题会放在: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)\) 次前缀信息。
如果我们可以平衡插入和查询的代价,我们就可以优化复杂度。