珂朵莉树总结
常常用于维护颜色段。随机数据下表现优秀,但构造数据随便卡。一定要看是否保证了数据随机。
前置
STL之set。年少不学STL,学珂朵莉树两行泪。
set内部是红黑树,内部不会出现值相同的元素。可重集使用multiset,用法基本与set一致。
插入删除
以下简写set<type>::iterator为iter
-
s.insert(x),插入值为x的元素,返回pair<iter,bool>类型,前一个是指向插入元素的指针,后一个是返回插入是否成功(set中已有等值元素时插入失败)。 -
s.erase(x),删除值为x的所有元素,返回删除的元素个数。 -
s.erase(iter p),删除迭代器为p的元素,要求迭代器合法。 -
s.erase(iter l,iter r),删除迭代器在\([l,r)\)的元素。 -
s.clear(),清空s。
迭代器
-
begin()/cbegin(),指向首元素的迭代器。 -
end()/cend(),指向尾端占位符的迭代器,注意此处没有元素。 -
rbegin()/crbegin(),指向逆向的首元素,可以理解为最后一个元素。 -
rend()/crend(),指向逆向的尾端占位符,对应正向首元素的前一个位置,注意此处没有元素。
以上带c的迭代器为只读类型。
查找
-
s.count(x),返回键值为x的元素个数。 -
s.find(x),返回值为x的元素的迭代器,如果不存在则返回end()。 -
s.lower_bound(x),返回s中首个不小于x的元素的迭代器,如果不存在则返回end()。\(O(\log n)\),但是如果用lower_bound(s.begin(),s.end(),x)则\(O(n)\)。 -
s.upper_bound(x),返回s中首个大于x的元素的迭代器,如果不存在则返回end()。复杂度同上。 -
set没有自带的nth_element,于是只能手写平衡树或者权值线段树或者pb_ds来\(O(\log n)\)查询第\(k\)大。直接使用nth_element(s.begin(),s.end(),k)是\(O(n)\)的。 -
s.empty(),返回容器是否为空;s.size(),返回容器内元素个数。
正式开始
结点
这样定义珂朵莉树的结点:
struct node{
int l,r;
mutable ll v;
node(const int &_l,const int &_r=-1,const ll &_v=0): ````l(_l),r(_r),v(_v){}
bool operator<(const node &t) const{
return l<t.l;
}
};
我们使用set<node> odt来表示一棵珂朵莉树。``
接下来是核心操作。
\(\texttt{split}\)
给出一段区间\([l,r]\)和\(pos\),要将其分割成\([l,pos-1],[pos,r]\)。
-
先在
s中lower_bound找到包含\(pos\)的区间。 -
如果\(pos\)是一个区间的左端点,那么不管。
-
否则删除原来的结点,然后插入两个新结点。
返回分裂后右边区间的迭代器。
auto split(int x) {
auto it=odt.lower_bound(node(x,0,0));
if (it!=odt.end()&&it->l==x) return it;
--it;
int l=it->l,r=it->r,v=it->v;
odt.erase(it);
odt.insert(node(l,x-1,v));
return odt.insert(node(x,r,v)).first;
}
\(\texttt{assign}\)
区间推平操作。防止段数过多导致复杂度退化成暴力。
将区间\([l,r]\)全部推平。就是先把\(l,r\)的迭代器分裂出来,然后把这个区间内的迭代器全部删除,再插入一个。
void assign(int l,int r,int val){
iter itr=split(r+1),itl=split(l);
odt.erase(itl,itr);
odt.insert((node){l,r,val});
}
必须先split(r+1),再split(l),因为这样必然不会删掉以\(l\)作为左区间的元素。而先分裂左端点,在分裂右端点时,可能因为左右端点在同一个段里面导致元素被删除而迭代器失效。
\(\texttt{perform}\)
将一段区间提取出来进行操作,与\(\texttt{assign}\)类似。只是将删除改为遍历。
复杂度
\(\texttt{perform}\)之后立即\(\texttt{assign}\)
数据随机下,是均摊\(O(m\log n)\),其中\(m\)是操作次数,\(n\)为结点个数,可以认为\(n\le m\)。
\(\texttt{perform}\)之后不调用\(\texttt{assign}\)
只有在随机数据下正确。set实现的复杂度为\(O(n\log \log n)\),list实现是\(O(n\log n)\)。

浙公网安备 33010602011771号