珂朵莉树,又叫ODT(Old Driver Tree),所需基础:会用set就行,它是一种基于set的暴力数据结构,常用于区间推平操作中(将一整段区间变成同一个数),来源于Codeforces的某场比赛题。
珂朵莉树的节点
一个珂朵莉树的节点一般维护一个区间的三个值,区间的左端点、右端点以及区间内的值(整个区间的值相同),我们直接用一个struct
就行,为了便于后面的操作,我们还需要对小于号重载,因为后面我们要按照区间的左端点排序。
#include<set>
using namespace std;
using LL = long long;
struct node {
int l, r;
LL val;
node(int left, int right = -1, LL v = 0) :l(left), r(right), val(v) {};//构造函数
bool operator<(const node& no)const {
return l < no.l;
}
};
set<node> s;
区间的分割
在使用珂朵莉树时,我们经常会用到分割操作,通常我们的函数会接收一个位置值pos
,假设pos
在区间l~r
之间,那么我们就把这个区间分割为l~pos-1
和pos~r
两部分,并返回pos~r
区间的位置,而如果存在区间pos~r
的话我们就直接返回该区间的位置。至于这个操作有什么用?后面就知道了。
using IT=set<node>::iterator;
IT split(int pos) {
IT it = s.lower_bound(node(pos));//找到l大于等于pos的一个区间
if (it != s.end() && it->l == pos) return it;//如果该区间左端点就是pos,就不用分割了
--it;//否则it--得到pos所在区间
int l = it->l, r = it->r;
LL val = it->val;
s.erase(it);
s.insert(node(l, pos - 1, val));
return s.insert(node(pos, r, val)).first;//set容器insert函数的返回值为pair,第一个元素是插入元素的位置,第二个元素是bool变量表示是否成功插入,如果插入前集合中已经有这个元素了则算做插入失败
}
区间推平
我们前面讲到了split
操作,至于它有什么作用,在这里我们将会知晓。区间推平操作也就是把l~r
内的值全部修改为val
,接下来我们直接看这个函数。
void assign(int l, int r, LL val) {
//注意,这里只能先split(r+1),因为如果先split(l)的话,我们再split(r+1)的时候就可能erase掉itl的位置,然后就RE了
IT itr = split(r + 1), itl = split(l);//我们把区间分成若干段,并返回左端点为r+1的区间和左端点为l的区间的位置
s.erase(itl, itr);//删除[itl,itr)之间的所有元素,即删除了区间l~r
s.insert(node(l, r, val));//插入区间l~r
}
这样我们就完成了珂朵莉树的基本操作。至于如何在题目中使用,还要多加刷题练习。