【学习笔记】珂朵莉树(ODT)
珂朵莉树
$\tt 0x00$ 起源
起源于 CodeForces 的一题 CF896C,当时出题人提供了这种做法,在随机数据下均摊复杂度比较优秀。
正统名字好像叫颜色段均摊,由于题目也得名于 $\overset{\tt{Old}}{\texttt{珂}}\overset{\tt{Driver}}{\texttt{朵}}\overset{\tt{Tree}}{\texttt{莉}}\texttt{树}$。
$\tt 0x01$ 基本结构
先看代码:
1 | struct node{ |
结构体中的 $l,r$ 指数列中 $[l,r]$ 段的左右端点,$v$ 指这一段表示的数字。
$\tt{mutable}$ 可让只读迭代器修改其中 $v$ 的值。
重载运算符 <
的用意是让所有区间按照左端点从小到大排序。
例如原数列为
经过颜色均摊之后,形成的珂朵莉树是这样的:
$\tt 0x02$ split
珂朵莉数核心操作:split
,作用是以 $pos$ 为分界线,把 $[l,r]$ 分为 $[l,pos-1]$ 和 $[pos,r]$ 两段,函数的返回值是指向 $[pos,r]$ 的迭代器。
为省时间且好写,set<node>::iterator
可以 typedef
一下或直接用 auto
(C++14 及以后)。
代码:
1 | auto split(int pos){ |
$\tt 0x03$ assign
对应区间推平操作。因为我们的 $[l,r]$ 可能包含在其他区间内,所以我们要先把 $l,r$ split
出来,然后删除中间的所有节点,最后插入一个 $(l,r,v)$ 即可。
注意分裂时要先 split(r+1)
再 split(l)
,不然可能会导致原来指向 split(l)
的迭代器释放,造成 RE。
代码:
1 | void assign(ll l,ll r,ll x){ |
$\tt 0x04$ 其他操作
基本都是套板子:
1 | void modify(ll l,ll r,ll x){ |
$\tt 0x05$ 例题
- https://www.luogu.com.cn/problem/CF896C (梦开始的地方)
- https://www.luogu.com.cn/problem/SP13015 (前置知识:素数筛)
- https://www.luogu.com.cn/problem/SP19568 (上一题加强版,加了单点修改操作)
- https://www.luogu.com.cn/problem/CF915E (珂朵莉树板子)