Some authors, e.g. Cormen & al.,2claim "the root is black" as fifth requirement; but not Mehlhorn & Sanders3or Sedgewick & Wayne.4Since the root can always be changed from red to black, this rule has little effect on analysis. This article also omits it, because it slightly disturbs the recursive algorithms and proofs.
/** * An RBTree-based set implementation * * @tparam key_t key type * @tparam compare_t compare function */template<typenamekey_t,typenamecompare_t=std::less<key_t>>structrb_tree{/** * Tree node */structnode_t{node_t*fa;// == nullptr if root of the tree, otherwise parentnode_t*ch[2];// == nullptr if child is empty// ch[0]: left child, ch[1]: right childkey_tdata;size_tsz;// Size of subtreeboolred;// == true if node is red, otherwise black/** * Get child direction of current non-null node * * @return true if this node is right child of its parent, otherwise false */autochild_dir()const->bool{returnthis==fa->ch[1];}};usingpointer=node_t*;usingconst_pointer=constnode_t*;usingpointer_const=node_t*const;constcompare_tcompare;pointerroot;rb_tree():compare{},root{nullptr}{}// ...};
/** * @param p root of subtree (may be same as {@code root}) * @param dir direction. 0: left rotate; 1: right rotate * @return new root of subtree */autorotate(pointerp,booldir)->pointer{autog=p->fa;autos=p->ch[!dir];// new root of subtreeassert(s);// pointer to true node requireds->sz=p->sz,p->sz=size(p->ch[dir])+size(s->ch[dir])+1;autoc=s->ch[dir];if(c)c->fa=p;p->ch[!dir]=c,s->ch[dir]=p;p->fa=s,s->fa=g;if(g)g->ch[p==g->ch[1]]=s;elseroot=s;returns;}
/** * @return nullptr if insert failed, otherwise pointer of inserted node */autoinsert(constkey_t&data)->const_pointer{pointern=newnode_t;n->fa=n->ch[0]=n->ch[1]=nullptr;n->data=data,n->sz=1;pointernow=root,p=nullptr;booldir=0;while(now){p=now;dir=compare(now->data,data);now=now->ch[dir];}insert_fixup_leaf(p,n,dir);returnn;}/** * Insert leaf node {@code n} to {@code p} * * @param p parent of node which will be inserted * @param n leaf node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_leaf(pointer_constp,pointer_constn,booldir){if(!p){root=n;return;}p->ch[dir]=n,n->fa=p;autonow=p;while(now)now->sz++,now=now->fa;}/** * Insert leaf node {@code n} to {@code p}, then fixup * * @param p parent of node which will be inserted * @param n node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_fixup_leaf(pointerp,pointern,booldir){n->red=p;insert_leaf(p,n,dir);// Fix double red// ...// Post process: color root blackroot->red=false;}
// Case 1: both p and u are red// g [g]// / \ / \ // [p] [u] ==> p u// / /// [n] [n]if(is_red(u)){p->red=u->red=false;g->red=true;n=g;continue;}
Insert case 2
𝑝 为红色,𝑢 为黑色,𝑝 的方向和 𝑛 的方向不同。
此时我们需要旋转 𝑝 节点来转为第三种情况。
实现
12345678
// p is red and u is black// Case 2: dir of n is different with dir of p// g g// / \ / \ // [p] u ==> [n] u// \ /// [n] [p]if(n->child_dir()!=p_dir)rotate(p,p_dir),std::swap(n,p);
Insert case 3
𝑝 为红色,𝑢 为黑色,𝑝 的方向和 𝑛 的方向相同。
此时我们需要旋转 𝑔 节点以将 𝑝 转为子树的根,之后交换 𝑝 和 𝑔 的颜色即可。
实现
12345678
// Case 3: p is red, u is black and dir of n is same as dir of p// g p// / \ / \ // [p] u ==> [n] [g]// / \ // [n] up->red=false,g->red=true;rotate(g,!p_dir);
/** * @return succeed or not */autoerase(constkey_t&key)->bool{autop=lower_bound(key);if(!p||p->data!=key)returnfalse;erase(p);returntrue;}/** * @return {@code next(p)} */autoerase(pointerp)->const_pointer{if(!p)returnnullptr;pointerresult;if(p->ch[0]&&p->ch[1]){autos=leftmost(p->ch[1]);std::swap(s->data,p->data);result=p,p=s;}elseresult=next(p);erase_fixup_branch_or_leaf(p);deletep;returnresult;}/** * Erase node {@code n} * * @param n node which will be deleted, must have no more than 2 child */voiderase_branch_or_leaf(pointer_constn){autop=n->fa,s=n->ch[0]?n->ch[0]:n->ch[1];if(s)s->fa=p;if(!p){root=s;return;}p->ch[n->child_dir()]=s;autonow=p;while(now)now->sz--,now=now->fa;}/** * Erase node {@code n}, then fixup * * @param n node which will be deleted, must have no more than 2 child */voiderase_fixup_branch_or_leaf(pointern){booln_dir=n==root?false:n->child_dir();erase_branch_or_leaf(n);autop=n->fa;if(!p){// n is rootif(root)root->red=false;return;}else{autos=p->ch[n_dir];if(s){// n has 1 child// n must be black and s must be red, so we need to color s blacks->red=false;return;}}// n is not root but leaf with black color, need to be fixup// ...// Post process: see case 2 & case 4n->red=false;}
while(p&&!n->red){autos=p->ch[!n_dir];// Delete case 1// ...// Other cases// s must be blackautoc=s->ch[n_dir],d=s->ch[!n_dir];// ...end_erase_fixup:p=n->fa;if(!p)break;n_dir=n->child_dir();}
Delete case 1
𝑠 为红色。
此时我们旋转 𝑝,将 𝑠 转为子树根节点,之后交换 𝑠 和 𝑝 的颜色来转为其余三种情况之一。
实现
1 2 3 4 5 6 7 8 91011
// Case 1: s is red// p s// / \ / \ // |n| [s] ==> [p] d// / \ / \ // c d |n| cif(is_red(s)){s->red=false,p->red=true;rotate(p,n_dir);s=p->ch[!n_dir];}
// Case 2: both c and d are black// {p} {p}// / \ / \ // |n| s ==> |n| [s]// / \ / \ // c d c d// p will be colored black in the endif(!is_red(c)&&!is_red(d)){s->red=true;n=p;gotoend_erase_fixup;}
// Case 3: c is red and d is black// {p} {p}// / \ / \ // |n| s ==> |n| c// / \ \ // [c] d [s]// \ // dif(!is_red(d)){c->red=false,s->red=true;rotate(s,!n_dir);s=p->ch[!n_dir],c=s->ch[n_dir],d=s->ch[!n_dir];}
/** * @file rbtree.hpp * @brief An RBTree-based set implementation * @details The set is sorted according to the {@code compare_t} function * provided; This implementation provides find, insert, remove, find order, find * key by order in O(log(n)) time. * @author [Tiphereth-A](https://github.com/Tiphereth-A) */#ifndef RBTREE_HPP#define RBTREE_HPP#include<cassert>#include<cstdint>#include<functional>usingstd::size_t;/** * An RBTree-based set implementation * * @tparam key_t key type * @tparam compare_t compare function */template<typenamekey_t,typenamecompare_t=std::less<key_t>>structrb_tree{/** * Tree node */structnode_t{node_t*fa;// == nullptr if root of the tree, otherwise parentnode_t*ch[2];// == nullptr if child is empty// ch[0]: left child, ch[1]: right childkey_tdata;size_tsz;// Size of subtreeboolred;// == true if node is red, otherwise black/** * Get child direction of current non-null node * * @return true if this node is right child of its parent, otherwise false */autochild_dir()const->bool{returnthis==fa->ch[1];}};usingpointer=node_t*;usingconst_pointer=constnode_t*;usingpointer_const=node_t*const;constcompare_tcompare;pointerroot;rb_tree():compare{},root{nullptr}{}~rb_tree(){post_order([](autoit){deleteit;});}autosize()const->size_t{returnsize(root);}template<typenameF>voidpre_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;callback(p),f(f,p->ch[0]),f(f,p->ch[1]);};f(f,root);}template<typenameF>voidin_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;f(f,p->ch[0]),callback(p),f(f,p->ch[1]);};f(f,root);}template<typenameF>voidpost_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;f(f,p->ch[0]),f(f,p->ch[1]),callback(p);};f(f,root);}autoleftmost(const_pointerp)const{returnmost(p,0);}autorightmost(const_pointerp)const{returnmost(p,1);}autoprev(const_pointerp)const{returnneighbour(p,0);}autonext(const_pointerp)const{returnneighbour(p,1);}autolower_bound(constkey_t&key)const->pointer{const_pointernow=root,ans=nullptr;while(now){if(!compare(now->data,key))ans=now,now=now->ch[0];elsenow=now->ch[1];}return(pointer)ans;}autoupper_bound(constkey_t&key)const->pointer{const_pointernow=root,ans=nullptr;while(now){if(compare(key,now->data))ans=now,now=now->ch[0];elsenow=now->ch[1];}return(pointer)ans;}// Order start from 0autoorder_of_key(constkey_t&key)const->size_t{size_tans=0;autonow=root;while(now){if(!compare(now->data,key))now=now->ch[0];elseans+=size(now->ch[0])+1,now=now->ch[1];}returnans;}// Order start from 0autofind_by_order(size_torder)const->const_pointer{const_pointernow=root,ans=nullptr;while(now&&now->sz>=order){autolsize=size(now->ch[0]);if(order<lsize)now=now->ch[0];else{ans=now;if(order==lsize)break;now=now->ch[1],order-=lsize+1;}}returnans;}/** * @return nullptr if insert failed, otherwise pointer of inserted node */autoinsert(constkey_t&data)->const_pointer{pointern=newnode_t;n->fa=n->ch[0]=n->ch[1]=nullptr;n->data=data,n->sz=1;pointernow=root,p=nullptr;booldir=0;while(now){p=now;dir=compare(now->data,data);now=now->ch[dir];}insert_fixup_leaf(p,n,dir);returnn;}/** * @return succeed or not */autoerase(constkey_t&key)->bool{autop=lower_bound(key);if(!p||p->data!=key)returnfalse;erase(p);returntrue;}/** * @return {@code next(p)} */autoerase(pointerp)->const_pointer{if(!p)returnnullptr;pointerresult;if(p->ch[0]&&p->ch[1]){autos=leftmost(p->ch[1]);std::swap(s->data,p->data);result=p,p=s;}elseresult=next(p);erase_fixup_branch_or_leaf(p);deletep;returnresult;}private:staticautosize(const_pointerp)->size_t{returnp?p->sz:0;}staticautois_red(const_pointerp)->bool{returnp?p->red:false;}/** * @param dir 0: leftmost, 1: rightmost */automost(const_pointerp,booldir)const->pointer{if(!p)returnnullptr;while(p->ch[dir])p=p->ch[dir];return(pointer)p;}/** * @param dir 0: prev, 1: next */autoneighbour(const_pointerp,booldir)const->pointer{if(!p)returnnullptr;if(p->ch[dir])returnmost(p->ch[dir],!dir);if(p==root)returnnullptr;while(p&&p->fa&&p->child_dir()==dir)p=p->fa;returnp?p->fa:nullptr;}/** * Insert leaf node {@code n} to {@code p} * * @param p parent of node which will be inserted * @param n leaf node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_leaf(pointer_constp,pointer_constn,booldir){if(!p){root=n;return;}p->ch[dir]=n,n->fa=p;autonow=p;while(now)now->sz++,now=now->fa;}/** * Erase node {@code n} * * @param n node which will be deleted, must have no more than 2 child */voiderase_branch_or_leaf(pointer_constn){autop=n->fa,s=n->ch[0]?n->ch[0]:n->ch[1];if(s)s->fa=p;if(!p){root=s;return;}p->ch[n->child_dir()]=s;autonow=p;while(now)now->sz--,now=now->fa;}/** * @param p root of subtree (may be same as {@code root}) * @param dir direction. 0: left rotate; 1: right rotate * @return new root of subtree */autorotate(pointerp,booldir)->pointer{autog=p->fa;autos=p->ch[!dir];// new root of subtreeassert(s);// pointer to true node requireds->sz=p->sz,p->sz=size(p->ch[dir])+size(s->ch[dir])+1;autoc=s->ch[dir];if(c)c->fa=p;p->ch[!dir]=c,s->ch[dir]=p;p->fa=s,s->fa=g;if(g)g->ch[p==g->ch[1]]=s;elseroot=s;returns;}#pragma GCC diagnostic ignored "-Wcomment"/** * Insert leaf node {@code n} to {@code p}, then fixup * * @param p parent of node which will be inserted * @param n node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_fixup_leaf(pointerp,pointern,booldir){n->red=p;insert_leaf(p,n,dir);// Fix double redwhile(is_red(p=n->fa)){boolp_dir=p->child_dir();autog=p->fa,u=g->ch[!p_dir];// Case 1: both p and u are red// g [g]// / \ / \ // [p] [u] ==> p u// / /// [n] [n]if(is_red(u)){p->red=u->red=false;g->red=true;n=g;continue;}// p is red and u is black// Case 2: dir of n is different with dir of p// g g// / \ / \ // [p] u ==> [n] u// \ /// [n] [p]if(n->child_dir()!=p_dir)rotate(p,p_dir),std::swap(n,p);// Case 3: p is red, u is black and dir of n is same as dir of p// g p// / \ / \ // [p] u ==> [n] [g]// / \ // [n] up->red=false,g->red=true;rotate(g,!p_dir);}// Post process: color root blackroot->red=false;}/** * Erase node {@code n}, then fixup * * @param n node which will be deleted, must have no more than 2 child */voiderase_fixup_branch_or_leaf(pointern){booln_dir=n==root?false:n->child_dir();erase_branch_or_leaf(n);autop=n->fa;if(!p){// n is rootif(root)root->red=false;return;}else{autos=p->ch[n_dir];if(s){// n has 1 child// n must be black and s must be red, so we need to color s blacks->red=false;return;}}// n is not root but leaf with black color, need to be fixupwhile(p&&!n->red){autos=p->ch[!n_dir];// Case 1: s is red// p s// / \ / \ // |n| [s] ==> [p] d// / \ / \ // c d |n| cif(is_red(s)){s->red=false,p->red=true;rotate(p,n_dir);s=p->ch[!n_dir];}// s must be blackautoc=s->ch[n_dir],d=s->ch[!n_dir];// Case 2: both c and d are black// {p} {p}// / \ / \ // |n| s ==> |n| [s]// / \ / \ // c d c d// p will be colored black in the endif(!is_red(c)&&!is_red(d)){s->red=true;n=p;gotoend_erase_fixup;}// Case 3: c is red and d is black// {p} {p}// / \ / \ // |n| s ==> |n| c// / \ \ // [c] d [s]// \ // dif(!is_red(d)){c->red=false,s->red=true;rotate(s,!n_dir);s=p->ch[!n_dir],c=s->ch[n_dir],d=s->ch[!n_dir];}// Case 4: d is red// {p} {s}// / \ / \ // |n| s ==> p d// / \ / \ // {c} [d] |n| {c}s->red=p->red,p->red=d->red=false;rotate(p,n_dir),n=root;end_erase_fixup:p=n->fa;if(!p)break;n_dir=n->child_dir();}// Post process: see case 2 & case 4n->red=false;}};#pragma GCC diagnostic warning "-Wcomment"#endif // RBTREE_HPP
#include<cassert>#include<cstdint>#include<functional>usingstd::size_t;/** * An RBTree-based set implementation * * @tparam key_t key type * @tparam compare_t compare function */template<typenamekey_t,typenamecompare_t=std::less<key_t>>structrb_tree{/** * Tree node */structnode_t{node_t*fa;// == nullptr if root of the tree, otherwise parentnode_t*ch[2];// == nullptr if child is empty// ch[0]: left child, ch[1]: right childkey_tdata;size_tsz;// Size of subtreeboolred;// == true if node is red, otherwise black/** * Get child direction of current non-null node * * @return true if this node is right child of its parent, otherwise false */autochild_dir()const->bool{returnthis==fa->ch[1];}};usingpointer=node_t*;usingconst_pointer=constnode_t*;usingpointer_const=node_t*const;constcompare_tcompare;pointerroot;rb_tree():compare{},root{nullptr}{}~rb_tree(){post_order([](autoit){deleteit;});}autosize()const->size_t{returnsize(root);}template<typenameF>voidpre_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;callback(p),f(f,p->ch[0]),f(f,p->ch[1]);};f(f,root);}template<typenameF>voidin_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;f(f,p->ch[0]),callback(p),f(f,p->ch[1]);};f(f,root);}template<typenameF>voidpost_order(Fcallback){autof=[&](auto&&f,pointerp){if(!p)return;f(f,p->ch[0]),f(f,p->ch[1]),callback(p);};f(f,root);}autoleftmost(const_pointerp)const{returnmost(p,0);}autorightmost(const_pointerp)const{returnmost(p,1);}autoprev(const_pointerp)const{returnneighbour(p,0);}autonext(const_pointerp)const{returnneighbour(p,1);}autolower_bound(constkey_t&key)const->pointer{const_pointernow=root,ans=nullptr;while(now){if(!compare(now->data,key))ans=now,now=now->ch[0];elsenow=now->ch[1];}return(pointer)ans;}autoupper_bound(constkey_t&key)const->pointer{const_pointernow=root,ans=nullptr;while(now){if(compare(key,now->data))ans=now,now=now->ch[0];elsenow=now->ch[1];}return(pointer)ans;}// Order start from 0autoorder_of_key(constkey_t&key)const->size_t{size_tans=0;autonow=root;while(now){if(!compare(now->data,key))now=now->ch[0];elseans+=size(now->ch[0])+1,now=now->ch[1];}returnans;}// Order start from 0autofind_by_order(size_torder)const->const_pointer{const_pointernow=root,ans=nullptr;while(now&&now->sz>=order){autolsize=size(now->ch[0]);if(order<lsize)now=now->ch[0];else{ans=now;if(order==lsize)break;now=now->ch[1],order-=lsize+1;}}returnans;}/** * @return nullptr if insert failed, otherwise pointer of inserted node */autoinsert(constkey_t&data)->const_pointer{pointern=newnode_t;n->fa=n->ch[0]=n->ch[1]=nullptr;n->data=data,n->sz=1;pointernow=root,p=nullptr;booldir=0;while(now){p=now;dir=compare(now->data,data);now=now->ch[dir];}insert_fixup_leaf(p,n,dir);returnn;}/** * @return succeed or not */autoerase(constkey_t&key)->bool{autop=lower_bound(key);if(!p||p->data!=key)returnfalse;erase(p);returntrue;}/** * @return {@code next(p)} */autoerase(pointerp)->const_pointer{if(!p)returnnullptr;pointerresult;if(p->ch[0]&&p->ch[1]){autos=leftmost(p->ch[1]);std::swap(s->data,p->data);result=p,p=s;}elseresult=next(p);erase_fixup_branch_or_leaf(p);deletep;returnresult;}private:staticautosize(const_pointerp)->size_t{returnp?p->sz:0;}staticautois_red(const_pointerp)->bool{returnp?p->red:false;}/** * @param dir 0: leftmost, 1: rightmost */automost(const_pointerp,booldir)const->pointer{if(!p)returnnullptr;while(p->ch[dir])p=p->ch[dir];return(pointer)p;}/** * @param dir 0: prev, 1: next */autoneighbour(const_pointerp,booldir)const->pointer{if(!p)returnnullptr;if(p->ch[dir])returnmost(p->ch[dir],!dir);if(p==root)returnnullptr;while(p&&p->fa&&p->child_dir()==dir)p=p->fa;returnp?p->fa:nullptr;}/** * Insert leaf node {@code n} to {@code p} * * @param p parent of node which will be inserted * @param n leaf node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_leaf(pointer_constp,pointer_constn,booldir){if(!p){root=n;return;}p->ch[dir]=n,n->fa=p;autonow=p;while(now)now->sz++,now=now->fa;}/** * Erase node {@code n} * * @param n node which will be deleted, must have no more than 2 child */voiderase_branch_or_leaf(pointer_constn){autop=n->fa,s=n->ch[0]?n->ch[0]:n->ch[1];if(s)s->fa=p;if(!p){root=s;return;}p->ch[n->child_dir()]=s;autonow=p;while(now)now->sz--,now=now->fa;}/** * @param p root of subtree (may be same as {@code root}) * @param dir direction. 0: left rotate; 1: right rotate * @return new root of subtree */autorotate(pointerp,booldir)->pointer{autog=p->fa;autos=p->ch[!dir];// new root of subtreeassert(s);// pointer to true node requireds->sz=p->sz,p->sz=size(p->ch[dir])+size(s->ch[dir])+1;autoc=s->ch[dir];if(c)c->fa=p;p->ch[!dir]=c,s->ch[dir]=p;p->fa=s,s->fa=g;if(g)g->ch[p==g->ch[1]]=s;elseroot=s;returns;}#pragma GCC diagnostic ignored "-Wcomment"/** * Insert leaf node {@code n} to {@code p}, then fixup * * @param p parent of node which will be inserted * @param n node which will be inserted * @param dir direction of n, 0: left; 1: right */voidinsert_fixup_leaf(pointerp,pointern,booldir){n->red=p;insert_leaf(p,n,dir);// Fix double redwhile(is_red(p=n->fa)){boolp_dir=p->child_dir();autog=p->fa,u=g->ch[!p_dir];// Case 1: both p and u are red// g [g]// / \ / \ // [p] [u] ==> p u// / /// [n] [n]if(is_red(u)){p->red=u->red=false;g->red=true;n=g;continue;}// p is red and u is black// Case 2: dir of n is different with dir of p// g g// / \ / \ // [p] u ==> [n] u// \ /// [n] [p]if(n->child_dir()!=p_dir)rotate(p,p_dir),std::swap(n,p);// Case 3: p is red, u is black and dir of n is same as dir of p// g p// / \ / \ // [p] u ==> [n] [g]// / \ // [n] up->red=false,g->red=true;rotate(g,!p_dir);}// Post process: color root blackroot->red=false;}/** * Erase node {@code n}, then fixup * * @param n node which will be deleted, must have no more than 2 child */voiderase_fixup_branch_or_leaf(pointern){booln_dir=n==root?false:n->child_dir();erase_branch_or_leaf(n);autop=n->fa;if(!p){// n is rootif(root)root->red=false;return;}else{autos=p->ch[n_dir];if(s){// n has 1 child// n must be black and s must be red, so we need to color s blacks->red=false;return;}}// n is not root but leaf with black color, need to be fixupwhile(p&&!n->red){autos=p->ch[!n_dir];// Case 1: s is red// p s// / \ / \ // |n| [s] ==> [p] d// / \ / \ // c d |n| cif(is_red(s)){s->red=false,p->red=true;rotate(p,n_dir);s=p->ch[!n_dir];}// s must be blackautoc=s->ch[n_dir],d=s->ch[!n_dir];// Case 2: both c and d are black// {p} {p}// / \ / \ // |n| s ==> |n| [s]// / \ / \ // c d c d// p will be colored black in the endif(!is_red(c)&&!is_red(d)){s->red=true;n=p;gotoend_erase_fixup;}// Case 3: c is red and d is black// {p} {p}// / \ / \ // |n| s ==> |n| c// / \ \ // [c] d [s]// \ // dif(!is_red(d)){c->red=false,s->red=true;rotate(s,!n_dir);s=p->ch[!n_dir],c=s->ch[n_dir],d=s->ch[!n_dir];}// Case 4: d is red// {p} {s}// / \ / \ // |n| s ==> p d// / \ / \ // {c} [d] |n| {c}s->red=p->red,p->red=d->red=false;rotate(p,n_dir),n=root;end_erase_fixup:p=n->fa;if(!p)break;n_dir=n->child_dir();}// Post process: see case 2 & case 4n->red=false;}};#pragma GCC diagnostic warning "-Wcomment"#include<iostream>#include<numeric>#include<vector>template<boolP6136>// == false: P3369, == true: P6136structIO_luogu_P3369_P6136{intlast_ans=0;std::size_tn,m;std::vector<int>a;std::vector<int>ans_list;voidinit(){std::cin>>n;if(!P6136){m=n;return;}std::cin>>m;a.resize(n);for(auto&i:a)std::cin>>i;}intopt(){intx;std::cin>>x;returnx;}intx(){intx;std::cin>>x;returnP6136?x^last_ans:x;}voidprint(intans){ans_list.push_back(last_ans=ans);}voidprint_total_ans(){if(P6136)std::cout<<std::accumulate(ans_list.begin(),ans_list.end(),0,std::bit_xor<int>{})<<'\n';elsefor(auto&&i:ans_list)std::cout<<i<<'\n';}};// 把这里的模板参数改为 false 即为「Luogu P3369【模板】普通平衡树」的代码IO_luogu_P3369_P6136<true>io;intmain(){std::cin.tie(nullptr)->sync_with_stdio(false);io.init();std::size_tn=io.n,m=io.m;rb_tree<std::pair<int,int>>bt;intcnt=0;for(autoi:io.a)bt.insert(std::make_pair(i,cnt++));for(inti=0,opt,x;(std::size_t)i<m;++i){opt=io.opt(),x=io.x();switch(opt){case1:bt.insert({x,cnt++});break;case2:bt.erase(bt.lower_bound({x,0}));break;case3:io.print((int)bt.order_of_key({x,0})+1);break;case4:io.print(bt.find_by_order((uint32_t)x-1)->data.first);break;case6:io.print(bt.upper_bound({x,n+m})->data.first);break;case5:autoit=bt.lower_bound({x,0});io.print((it?bt.prev(it):bt.rightmost(bt.root))->data.first);break;}}io.print_total_ans();return0;}
与 2-3-4 树的关系
2-3-4 树是 4 阶 B 树,与一般的 B 树一样,2-3-4 树可以实现在 𝑂(log𝑛) 时间内进行搜索、插入和删除操作。2-3-4 树的节点分为三种,2 节点、3 节点和 4 节点,分别包含一个、两个或三个数据元素。所有的叶子节点都处于同一深度(最底层),所有数据都有序存储。
Linux 的稳定内核版本在 2.6.24 之后,使用了新的调度程序 CFS,所有非实时可运行进程都以虚拟运行时间为键值用一棵红黑树进行维护,以完成更公平高效地调度所有任务。CFS 弃用 active/expired 数组和动态计算优先级,不再跟踪任务的睡眠时间和区别是否交互任务,而是在调度中采用基于时间计算键值的红黑树来选取下一个任务,根据所有任务占用 CPU 时间的状态来确定调度任务优先级。
L. J. Guibas and R. Sedgewick, "A dichromatic framework for balanced trees,"19th Annual Symposium on Foundations of Computer Science (sfcs 1978), Ann Arbor, MI, USA, 1978, pp. 8-21, doi:10.1109/SFCS.1978.3. ↩