珂朵莉树
(珂学)
珂朵莉树(或者老司机树)起源于CF896C。
由于之前做到每一组数据都要另外开数据结构,所以现在一些东西就会写为class包装
前置知识点
- STL中set的使用(list也行,但是效率差点)
- set的排序规则
- insert
- erase
- lower_bownd
- 暴力
使用
珂朵莉树通过直接对区间进行暴力维护。不同于线段树或者树状数组对于区间的维护,珂朵莉树相当于直接把区间拆出来进行修改。
下面的代码基于CF896C。
定义
珂朵莉树的定义如下:
// using ll = long long;
class ODT {
private:
class Node {
int l, r;
mutable ll val;
Node(int l = 0, int r = 0, ll val = 0) : l(l), r(r), val(val) {
}
bool operator < (const Node &a) const {
return l < a.l;
}
};
set<Node>tr;
public:
void insert(int l, int r, int val);
auto split(int pos);
void assign(int l, int r, ll val);
auto work(int l, int r, ...);
};
珂朵莉树是对区间的暴力维护,需要重载小于号(或者写个比较函数,只要像上面那个一样就行)(Node是想不到名字就起的,也可以改为Segment)。而后面的set就是珂朵莉树的核心。
关于mutable关键字
mutable
意为可变的,也就是说,加上这个关键字可以直接对里面的val
进行修改,而无需清除再插入
下面还有几个操作。
insert
其实这个操作仅仅是把区间插入set里面(因为没有的区间要插入),不过因为用class包装所以需要多一个insert。
void ODT::insert(int l, int r, int val) {
tr.insert(Node(l, r, val));
}
split
珂朵莉树的第一个核心操作。
这个操作就是直接暴力的把一个区间(根据区间左端点)拆出来。
auto ODT::split(int pos) {
auto it = tr.lower_bownd(Node(pos));
// 找到一个有效的区间,而且这个区间左端点刚好是位置
if (it != tr.end() && it->l == pos) {
return it;
}
// 到这里,说明找的区间位置偏右了点
-- it;
int l = it->l, r = it->r;
ll val = it->val;
// 暴力分裂
tr.erase(it);
tr.insert(Node(l, pos - 1, val));
return tr.insert(Node(pos, r, val)).first;
}
set的insert操作的first
set的insert操作中会返回一个pair。其中first为指向插入内容的迭代器。由于分裂的目的就是为了提出左端点为pos的区间,所以要返回这个玩意。
assign
这个是珂朵莉树的第二个关键操作。
这个操作就是简单暴力地,将l到r范围内的所有区间暴力删掉,然后新建一个区间l到r,值改为val。
void ODT::assign(int l, int r, ll val)