珂朵莉树是一种暴力数据结构,代码较短,原理简单。适用于骗分和暴力对拍。
适用范围
- 含有区间赋值操作。
- 操作随机或者区间赋值操作较多。
- 维护区间高次幂。
主要原理
把连续一段相同的值合并成一块,用set
维护这些块。
随机数据下期望复杂度$O(m\log n)$($m$为操作数,$n$为序列内数字个数)。
基本操作
Node
1 | struct Node { |
注意:用iterator
修改值时,该值必须为mutable
形式。默认是只读,不这样写不能编译通过。
split
把一个块分裂为两块。1
2
3
4
5
6
7
8
9
10
11
12set<Node> s;
typedef set<Node>::iterator Iter;
Iter split(int pos) {
Iter it = s.lower_bound(Node(pos));
if (it != s.end() && it->l == pos) // ***
return it;
--it;
int l = it->l, r = it->r, v = it->v; // ***
s.erase(it);
s.insert(Node(l, pos - 1, v));
return s.insert(Node(pos, r, v)).first;
}
默认的insert
是std::pair<iterator,bool> insert(const value_type &value)
,这里用它的返回值first
。
注意:
if
内的先后顺序,颠倒后会访问空指针出错。- 后面的
erase
和insert
会使当前的it
指针失效,指向其他地方,所以要先记下来这些值。 - 函数内含有
erase
,可能会使其他iterator
失效,调用的时候应注意这种情况。
assign
把连续相同数字的区间合并成一块。是珂朵莉在随机数据下复杂度的保证。
在随机数据下,每次合并的期望长度为$\frac{n}{3}$。
1 | void assign(int l, int r, int v) { |
void erase(iterator first, iterator last)
可删除[first,last)
区间。
注意:取区间的时候应当先取后面再取前面,否则取完前面后,后面的指针会发生变化。
其他操作
全是暴力,具体见代码。
题目
CF896C。也是珂朵莉树的来源。
1 |
|
再放一些其他能用珂朵莉树的题。
CF343D Water Tree
P2572 [SCOI2010]序列操作
P4344 [SHOI2015]脑洞治疗仪
CF915E Physical Education Lessons
P2787 语文1(chin1)- 理理思维
P4979 矿洞:坍塌
目前来看,大多数出题人都没有刻意去卡珂朵莉树,但是大部分情况下,由于数据不是随机,复杂度都是错的。
要谨慎使用。
2019.5.5更新:
P5350序列。
标算是珂朵莉树。这道题有copy
和swap
操作,会在其中删除一部分,再插入一部分。在右端点$r$已经被删除的情况下,如果下一次还要$split(r)$,就会产生类似于$[r, r - 1]$的错误区间,会对答案产生影响。
解决方法:先取小再取大;或者在出现这种情况的时候特判一下。因为已经$split$过右端点,只需要判断是否小于等于它就好,不需要比较指针了。
1 |
|