模拟 | 珂朵莉树:学习笔记
珂朵莉树:学习笔记
老司机树,ODT(Old Driver Tree),又名珂朵莉树(Chtholly Tree)。
起源自 CF896C 。因为这是第一道以此树 AC 的题,又是关于动漫角色 珂朵莉 的,故因此得名。
是一种非常暴力的数据结构,时间复杂度不好计算,且取决于数据的随机性,珂朵莉树可以用于带有区间覆盖的问题当中,且当覆盖操作比较多的时候,它能发挥出一定优势,所以珂朵莉树通常用于写不出正统解法时的骗分选择
要保证珂朵莉树的复杂度正确,数据必须随机(也就是出题人不会特意卡),证明
在很多题目中不是正统的解法,但是有些时候却比正统的解法跑得更快……
核心思想
珂朵莉树的核心思想是:把值相同的区间合并成一个结点保存在 set 里面。
因此一个树节点只包含三个信息:左右端点 $L,R$ ,和区间值 $v$,定义时注意值 $v$ 要用 mutable
修饰
1 | struct node{ |
其实核心思想就很暴力了……
一下是珂朵莉树的几种基本操作,无一例外地,实现它们的方式都非常暴力
关键操作:Split
split
是珂朵莉树的核心之一,用于从当前 set
中提取需要的区间结点便于操作,它会将一个大的区间结点 $[L,R]$ 分裂成两个区间结点,为 $[l,x-1]$ 和 $[x.R]$ ,一般情况下我们都会 split
两次,split(r+1);split(l)
注意一定要先 split(r+1)
,之后我们会取得两个迭代器,这两个迭代器之间存储的就是操作区间内的结点了
而关于 split
的写法也比较简单,就是暴力找到以 $x$ 作为左端点的结点,然后把原来的大结点直接 erase
掉,新插入两个小结点,返回后一个结点的迭代器
1 |
|
关键操作:fill(assign)
assign
是珂朵莉树用于处理区间覆盖问题的操作,能减少 set 中的结点个数,区间覆盖操作越多,set 中的节点个数越少,因此每次操作的时间复杂度会降低,这也是珂朵莉树在某些题目中跑的比正统解法快的原因之一吧
assign
操作本身非常暴力,代码也很简洁,很好理解,就是先 split
得到覆盖的左右端点对应的迭代器,然后直接 erase
掉,插入一个新的结点……
1 |
|
区间加:add
从区间加操作开始,以下操作全是基于 for
循环或 while
循环的纯暴力遍历来实现的
区间加法,就是 split
得到区间左右端点对应的迭代器,然后遍历这些区间,逐一加上就行了……
1 |
|
区间求和:sum
区间求和,也是先 split
然后再遍历,累加,注意中途要乘上区间长度,因为每个结点是一个值完全相同的区间
1 |
|
区间第K小:kth
个人感觉这是最暴力的操作了,因为这个操作不但遍历,而且中途还直接使用 vector
排序,计算的时候也要遍历 vector
……
1 | int kth(int l,int r,int k){ |
珂朵莉树就是这么暴力的一种数据结构,然而在一些涉及到很多次区间覆盖的问题中却能够跑的很快,果然还是 set 大法好啊……,下面是可以用珂朵莉树 AC 的题选
P.S:以下题目的正統解法几乎都是线段树,写法正确的线段树也通常比珂朵莉树跑的快一些
- SCOI2010 序列操作:珂朵莉树模板题,没什么好讲的
- SHOI2015 脑洞治疗仪:几乎是模板题,注意判断正常脑组织够不够填补脑洞即可
- LG2787 理理思维:模板题,输入字符串的时候尽量先把相同的字符合成一个结点
LG4979 矿洞坍塌:模板题,注意输出字符串的优化和题目要求的判断条件即可
USCAO1.2.1 挤牛奶:这个
弱智题也需要用珂朵莉树?是的!只要是区间覆盖都可以用它骗分