珂朵莉树
老师给的数据结构题单里出现的,会的同学教了一下。没有想象中的难。
思路
基础的珂朵莉树其实就是用 set 维护一个数组上的连续相同区间,每一个区间用一个结构体表示。
struct node
{
int l, r; //区间左右端点
mutable int v; //区间里每个数的值
friend bool operator < (node x, node y)
{
return x.l < y.l;
} //从左往右排好序
};
typedef set <node> :: iterator snodeit;
set <node> s;
基础操作
split
要处理某一个点 \(x\) 时,可以把这个点所在的区间拆成 \([l,x)\) 和 \([x,r]\) 并返回指向后者的迭代器,过程如下。
snodeit split(int pos)
{
snodeit it = s.lower_bound ({pos, 0, 0});//找到区间
if (it != s.end () && (*it).l == pos) return it;//如果左端点就是它就不用拆,直接返回
it--;//否则是上一个区间
if ((*it).r < pos) return s.end ();//特判pos=n+1的情况
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是有返回值的,是个pair,first就是迭代器
}
assign
找到要修改的区间,把这一段上的区间全都删掉并插入新的区间。
void assign(int l, int r, int x)
{
snodeit itr = split (r + 1), itl = split (l);//先把两头的区间给拆了
s.erase (itl, itr);//set 自带的函数,左闭右开
s.insert (node{l, r, x});
}
注意这里,如果要保证正确性,一定要先拆右边再拆左边,否则拆右边时可能会把 itl 指向的区间删了,会出问题。
这就是珂朵莉树的基础操作,区间加之类的和 assign 同理,只不过改成遍历 \([itl,itr)\),每个区间加 \(x\)。
注意这里的区间加,set 里的值是 \(const\) 的,不能直接修改,所以要像上面的结构体里那样,使用 mutable,这个可以使得 const 变量也可以修改。

浙公网安备 33010602011771号