珂朵莉树总结

常常用于维护颜色段。随机数据下表现优秀,但构造数据随便卡。一定要看是否保证了数据随机

前置

STL之set。年少不学STL,学珂朵莉树两行泪。

set内部是红黑树,内部不会出现值相同的元素。可重集使用multiset,用法基本与set一致。

插入删除

以下简写set<type>::iteratoriter

  • 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]\)

  • 先在slower_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)\)

posted @ 2024-12-25 20:48  RandomShuffle  阅读(31)  评论(0)    收藏  举报