Treap树

  核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn)

Treap模板:

 #include <cstdio>
 #include <cstring>
 #include <ctime>
 #include <iostream>
 #include <algorithm>
 #include <cstdlib>
 #include <cmath>
 #include <utility>
 #include <vector>
 #include <queue>
 #include <map>
 #include <set>
 #define max(x,y) ((x)>(y)?(x):(y))
 #define min(x,y) ((x)>(y)?(y):(x))
 #define INF 0x3f3f3f3f
 #define MAXN 100005

 using namespace std;

 ,rt=; //节点编号从1开始

 struct Tree
 {
     ]; //保证父亲的pri大于儿子的pri
     void set(int x, int y, int z)
     {
         key=x;
         pri=y;
         size=z;
         son[]=son[]=;
     }
 }T[MAXN];

 void rotate(int p, int &x)
 {
     int y=T[x].son[!p];
     T[x].size=T[x].size-T[y].size+T[T[y].son[p]].size;
     T[x].son[!p]=T[y].son[p];
     T[y].size=T[y].size-T[T[y].son[p]].size+T[x].size;
     T[y].son[p]=x;
     x=y;
 }

 void ins(int key, int &x)
 {
     )
         T[x = cnt++].);
     else
     {
         T[x].size++;
         int p=key < T[x].key;
         ins(key, T[x].son[!p]);
         if(T[x].pri < T[T[x].son[!p]].pri)
             rotate(p, x);
     }
 }

 void del(int key, int &x) //删除值为key的节点
 {
     if(T[x].key == key)
     {
         ] && T[x].son[])
         {
             ]].pri > T[T[x].son[]].pri;
             rotate(p, x);
             del(key, T[x].son[p]);
         }
         else
         {
             ])
                 x=T[x].son[];
             else
                 x=T[x].son[];
         }
     }
     else
     {
         T[x].size--;
         int p=T[x].key > key;
         del(key, T[x].son[!p]);
     }
 }

 int find(int p, int &x) //找出第p小的节点的编号
 {
     ]].size+)
         return x;
     ]].size+)
         find(p-T[T[x].son[]].size-, T[x].son[]);
     else
         find(p, T[x].son[]);
 }

 int find_NoLarger(int key, int &x) //找出值小于等于key的节点个数
 {
     )
         ;
     if(T[x].key <= key)
         ]].size++find_NoLarger(key, T[x].son[]);
     else
         ]);
 }

相关题解:

POJ 3481 treap

POJ 1442 treap

POJ 2352 treap

Splay Tree(伸展树)

  核心就是 过程Splay(x, y),即将x节点转移到y节点的子节点上面(其中y是x的祖先)。

  利用其中双旋的优势能够保证查询复杂度均摊为O(lgn)

  一开始理解有些困难,其实实际上不做深入的理解就是,双旋的过程就是一个建立相对平衡的二叉树的一个过程。

  》对于二叉树,最极端的情况就是线性插入,使得整棵二叉树退化为一条链。比如你查询链的最后一个节点,之后再次查询第一个节点。

    1)若只是单旋通过Splay(x, 0)将最后一个节点移动到根节点,需要O(n)复杂度,而查询第一个节点时又需要O(n)复杂度,来来往往就退化成一条链了。

    2)若是双旋Splay(x, 0)将最后一个节点移动到根节点上时,移动过程中建立起了相对平衡的二叉树,需要O(n),也就是查询第一个节点时,大概是需要O(lgn)复杂度。这就降低了复杂度。可以证明,总的每个操作的均摊复杂度是O(lgn)。

    具体证明可以参见 杨思雨《伸展树的基本操作与应用》

I 用于维护单调队列:(以key为维护对象保证单调)

常用版:(支持相同值)

Struct Tree{

  int key, size, fa, son[2];

}

void PushUp(int x);

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To) //将x节点插入到To的子节点中

int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处

int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处

int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处

void Insert(int key) //插入key 并且将该节点转移到根处

void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处

int GetPth(int p) //获得第p小的节点 并将其转移到根处

int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<

 , rt=;

 struct Tree
 {
     ];
     void set(int _key, int _size, int _fa)
     {
         key=_key;
         size=_size;
         fa=_fa;
         son[]=son[]=;
     }
 }T[MAXN];

 inline void PushUp(int x)
 {
     T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+;
 }

 inline void Rotate(int x, int p) //0左旋 1右旋
 {
     int y=T[x].fa;
     T[y].son[!p]=T[x].son[p];
     T[T[x].son[p]].fa=y;
     T[x].fa=T[y].fa;
     if(T[x].fa)
         T[T[x].fa].son[T[T[x].fa].son[] == y]=x;
     T[x].son[p]=y;
     T[y].fa=x;
     PushUp(y);
     PushUp(x);
 }

 void Splay(int x, int To) //将x节点插入到To的子节点中
 {
     while(T[x].fa != To)
     {
         if(T[T[x].fa].fa == To)
             Rotate(x, T[T[x].fa].son[] == x);
         else
         {
             int y=T[x].fa, z=T[y].fa;
             ] == y);
             if(T[y].son[p] == x)
                 Rotate(x, !p), Rotate(x, p); //之字旋
             else
                 Rotate(y, p), Rotate(x, p); //一字旋
         }
     }
     ) rt=x;
 }

 int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
 {
     int x=rt;
     while(x && T[x].key != key)
         x=T[x].son[key > T[x].key];
     );
     return x;
 }

 int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处
 {
     ];
     ;
     ])
         x=T[x].son[];
     Splay(x, );
     return x;
 }

 int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处
 {
     ];
     ;
     ])
         x=T[x].son[];
     Splay(x, );
     return x;
 }

 void Insert(int key) //插入key 并且将该节点转移到根处
 {
     if(!rt)
         T[rt = cnt++]., );
     else
     {
         ;
         while(x)
         {
             y=x;
             x=T[x].son[key > T[x].key];
         }
         T[x = cnt++]., y);
         T[y].son[key > T[y].key]=x;
         Splay(x, );
     }
 }

 void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处
 {
     int x=find(key);
     if(!x) return;
     ];
     ])
         y=T[y].son[];
     ];
     ])
         z=T[z].son[];
     if(!y && !z)
     {
         rt=;
         return;
     }
     if(!y)
     {
         Splay(z, );
         T[z].son[]=;
         PushUp(z);
         return;
     }
     if(!z)
     {
         Splay(y, );
         T[y].son[]=;
         PushUp(y);
         return;
     }
     Splay(y, );
     Splay(z, y);
     T[z].son[]=;
     PushUp(z);
     PushUp(y);
 }

 int GetPth(int p) //获得第p小的节点 并将其转移到根处
 {
     ;
     ;
     while(x)
     {
         ]].size+)
             break;
         ]].size+)
         {
             p-=T[T[x].son[]].size+;
             x=T[x].son[];
         }
         else
             x=T[x].son[];
     }
     Splay(x, );
     return x;
 }

 int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
 {
     ;
     , y;
     while(x)
     {
         y=x;
         if(T[x].key <= key)
         {
             ret+=T[T[x].son[]].size+;
             x=T[x].son[];
         }
         else
             x=T[x].son[];
     }
     Splay(y, );
     return ret;
 }

完全版:(支持相同值,支持区间删除,支持懒惰标记)

Struct Tree{

  int key, num, size, fa, son[2];

}

void PushUp(int x);

void PushDown(int x);

int Newnode(int key, int fa); //新建一个节点并返回

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int GetPth(int p, int To); //返回第p小的节点 并移动到To的子节点中

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int key); //插入key值

void Delete(int key); //删除值为key的节点

int GetRank(int key); //获得值<=key的节点个数

void Delete(int l, int r); //删除值在[l, r]中的节点

 int cnt, rt;
 int Add[MAXN];

 struct Tree{
     ];
 }T[MAXN];

 inline void PushUp(int x)
 {
     T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+T[x].num;
 }

 inline void PushDown(int x)
 {
     if(Add[x])
     {
         ])
         {
             T[T[x].son[]].key+=Add[x];
             Add[T[x].son[]]+=Add[x];
         }
         ])
         {
             T[T[x].son[]].key+=Add[x];
             Add[T[x].son[]]+=Add[x];
         }
         Add[x]=;
     }
 }

 inline int Newnode(int key, int fa) //新建一个节点并返回
 {
     ++cnt;
     T[cnt].key=key;
     T[cnt].num=T[cnt].size=;
     T[cnt].fa=fa;
     T[cnt].son[]=T[cnt].son[]=;
     return cnt;
 }

 inline void Rotate(int x, int p) //0左旋 1右旋
 {
     int y=T[x].fa;
     PushDown(y);
     PushDown(x);
     T[y].son[!p]=T[x].son[p];
     T[T[x].son[p]].fa=y;
     T[x].fa=T[y].fa;
     if(T[x].fa)
         T[T[x].fa].son[T[T[x].fa].son[] == y]=x;
     T[x].son[p]=y;
     T[y].fa=x;
     PushUp(y);
     PushUp(x);
 }

 void Splay(int x, int To) //将x节点移动到To的子节点中
 {
     while(T[x].fa != To)
     {
         if(T[T[x].fa].fa == To)
             Rotate(x, T[T[x].fa].son[] == x);
         else
         {
             int y=T[x].fa, z=T[y].fa;
             ] == y);
             if(T[y].son[p] == x)
                 Rotate(x, !p), Rotate(x, p); //之字旋
             else
                 Rotate(y, p), Rotate(x, p); //一字旋
         }
     }
     ) rt=x;
 }

 int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
 {
     ;
     int x=rt;
     while(x)
     {
         PushDown(x);
         ]].size+ && p <= T[T[x].son[]].size+T[x].num)
             break;
         ]].size+T[x].num)
         {
             p-=T[T[x].son[]].size+T[x].num;
             x=T[x].son[];
         }
         else
             x=T[x].son[];
     }
     Splay(x, );
     return x;
 }

 int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
 {
     ;
     int x=rt;
     while(x)
     {
         PushDown(x);
         if(T[x].key == key) break;
         x=T[x].son[key > T[x].key];
     }
     );
     return x;
 }

 int Prev() //返回根节点的前驱 非重点
 {
     ]) ;
     ];
     ])
     {
         PushDown(x);
         x=T[x].son[];
     }
     Splay(x, );
     return x;
 }

 int Succ() //返回根结点的后继 非重点
 {
     ]) ;
     ];
     ])
     {
         PushDown(x);
         x=T[x].son[];
     }
     Splay(x, );
     return x;
 }

 void Insert(int key) //插入key值
 {
     if(!rt)
         rt=Newnode(key, );
     else
     {
         ;
         while(x)
         {
             PushDown(x);
             y=x;
             if(T[x].key == key)
             {
                 T[x].num++;
                 T[x].size++;
                 break;
             }
             T[x].size++;
             x=T[x].son[key > T[x].key];
         }
         if(!x)
             x=T[y].son[key > T[y].key]=Newnode(key, y);
         Splay(x, );
     }
 }

 void Delete(int key) //删除值为key的节点1个
 {
     int x=Find(key);
     if(!x) return;
     )
     {
         T[x].num--;
         PushUp(x);
         return;
     }
     ];
     ])
         y=T[y].son[];
     ];
     ])
         z=T[z].son[];
     if(!y && !z)
     {
         rt=;
         return;
     }
     if(!y)
     {
         Splay(z, );
         T[z].son[]=;
         PushUp(z);
         return;
     }
     if(!z)
     {
         Splay(y, );
         T[y].son[]=;
         PushUp(y);
         return;
     }
     Splay(y, );
     Splay(z, y);
     T[z].son[]=;
     PushUp(z);
     PushUp(y);
 }

 int GetRank(int key) //获得值<=key的节点个数
 {
     if(!Find(key))
     {
         Insert(key);
         ]].size;
         Delete(key);
         return tmp;
     }
     else
         ]].size+T[rt].num;
 }

 void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
 {
     if(!Find(l)) Insert(l);
     int p=Prev();
     if(!Find(r)) Insert(r);
     int q=Succ();
     if(!p && !q)
     {
         rt=;
         return;
     }
     if(!p)
     {
         T[rt].son[]=;
         PushUp(rt);
         return;
     }
     if(!q)
     {
         Splay(p, );
         T[rt].son[]=;
         PushUp(rt);
         return;
     }
     Splay(p, q);
     T[p].son[]=;
     PushUp(p);
     PushUp(q);
 }

(经测NOI2004郁闷的出纳员 POJ3481 POJ2352 POJ1442)

速度相对来说都还不错,POJ这些都3~500ms,郁闷的出纳员900多ms

相关题解:

HNOI 2002 营业额统计

POJ 3481 splay

POJ 2352 splay

POJ 1442 splay

NOI2004 郁闷的出纳员

II 用于维护序列:(以序列下标为对象维护,相当于对区间操作)(能够完成线段树的操作及其不能完成的操作)

Struct Tree{

  int key, sum, size, fa, son[2];

}

支持操作:

void PushUp(int x);

void PushDown(int x);

int MakeTree(int l, int r, int a[]); //新建一个子树返回根节点

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int Select(int p, int To); //将第p个数移动到To的子节点中 并返回该节点

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int p, int l, int r, int a[]) //将a[l .. r]的数插入到下标为p后面

void Delete(int l, int r); //删除区间[l, r]中的节点

int Query(int l, int r); //返回[l, r]的和

待补充。。

Size Balance Tree

  和上述两种二叉树比起来,SBT可能是最像真正平衡二叉树吧。

  SBT能够保证树的高度在lgn,这样对于插入,删除操作都能够准确保证时间复杂度在O(lgn)

  Maintain操作事实上理解起来也是挺简单的,至于证明参见CQF神牛的《SBT》

 int cnt, rt;

 struct Tree
 {
     ];
 }T[MAXN];

 inline void PushUp(int x)
 {
     T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+;
 }

 inline int Newnode(int key)
 {
     ++cnt;
     T[cnt].key=key;
     T[cnt].size=;
     T[cnt].son[]=T[cnt].son[]=;
     return cnt;
 }

 void Rotate(int p, int &x)
 {
     int y=T[x].son[!p];
     T[x].son[!p]=T[y].son[p];
     T[y].son[p]=x;
     PushUp(x);
     PushUp(y);
     x=y;
 }

 void Maintain(int &x, int p) //维护SBT的!p子树
 {
     if(T[T[T[x].son[p]].son[p]].size > T[T[x].son[!p]].size)
         Rotate(!p, x);
     else if(T[T[T[x].son[p]].son[!p]].size > T[T[x].son[!p]].size)
         Rotate(p, T[x].son[p]), Rotate(!p, x);
     else return;
     Maintain(T[x].son[], );
     Maintain(T[x].son[], );
     Maintain(x, );
     Maintain(x, );
 }

 inline int Prev() //返回比根值小的最大值 若无返回0
 {
     ];
     ;
     ])
         x=T[x].son[];
     return x;
 }

 inline int Succ() //返回比根值大的最小值 若无返回0
 {
     ];
     ;
     ])
         x=T[x].son[];
     return x;
 }

 void Insert(int key, int &x)
 {
     if(!x) x=Newnode(key);
     else
     {
         T[x].size++;
         Insert(key, T[x].son[key > T[x].key]);
         Maintain(x, key > T[x].key);
     }
 }

 bool Delete(int key, int &x) //删除值为key的节点 key可以不存在
 {
     ;
     if(T[x].key == key)
     {
         ])
         {
             x=T[x].son[];
             ;
         }
         ])
         {
             x=T[x].son[];
             ;
         }
         int y=Prev();
         T[x].size--;
         ]);
     }
     else
         if(Delete(key, T[x].son[key > T[x].key]))
         {
             T[x].size--;
             ;
         }
 }

 int GetPth(int p, int &x) //返回第p小的节点
 {
     ;
     ]].size+)
         return x;
     ]].size+)
         ]].size-, T[x].son[]);
     else
         ]);
 }

 int GetRank(int key, int &x) //找出值<=key的节点个数
 {
     ;
     if(T[x].key <= key)
         ]].size++GetRank(key, T[x].son[]);
     else
         ]);
 }

相关题解:

POJ 3481 SBT做法

上述题均为用于测试平衡树基本操作的题目。

提高题:(暂时未写)

[NOI2005]维修数列

[POJ3580]SuperMemo

[HNOI2004]宠物收养所

三大平衡树(Treap + Splay + SBT)总结+模板[转]的更多相关文章

  1. 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

    平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...

  2. 三大平衡树(Treap + Splay + SBT)总结+模板[转]

    Treap树 核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn) Treap模板: #include <cstdio> #include <cstring> #i ...

  3. 三大平衡树(Treap + Splay + SBT)总结+模板

    Treap树 核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn) Treap模板: #include <cstdio> #include <cstring> #i ...

  4. BZOJ 3224 - 普通平衡树 - [Treap][Splay]

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中 ...

  5. [洛谷P3369] 普通平衡树 Treap & Splay

    这个就是存一下板子...... 题目传送门 Treap的实现应该是比较正经的. 插入删除前驱后继排名什么的都是平衡树的基本操作. #include<cstdio> #include< ...

  6. luoguP3369[模板]普通平衡树(Treap/SBT) 题解

    链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) 平衡树解析 #include<iostream> #include<cstdlib> #includ ...

  7. 平衡树Treap模板与原理

    这次我们来讲一讲Treap(splay以后再更) 平衡树是一种排序二叉树(或二叉搜索树),所以排序二叉树可以迅速地判断两个值的大小,当然操作肯定不止那么多(不然我们还学什么). 而平衡树在排序二叉树的 ...

  8. P3369 【模板】普通平衡树(splay)

    P3369 [模板]普通平衡树 就是不用treap splay板子,好好背吧TAT #include<iostream> #include<cstdio> #include&l ...

  9. 算法模板——平衡树Treap 2

    实现功能:同平衡树Treap 1(BZOJ3224 / tyvj1728) 这次的模板有了不少的改进,显然更加美观了,几乎每个部分都有了不少简化,尤其是删除部分,这个参照了hzwer神犇的写法,在此鸣 ...

随机推荐

  1. PHP feof()函数

    feof()函数检查是否已经到达文件末尾(EOF) EOF == end  of  file 如果出错或者文件指针到了文件末尾(EOF)则返回true,否则返回false 语法: feof(file) ...

  2. Log4j日志依赖

    <!-- https://mvnrepository.com/artifact/log4j/log4j --><dependency> <groupId>log4j ...

  3. 【Python】【容器 | 迭代对象 | 迭代器 | 生成器 | 生成器表达式 | 协程 | 期物 | 任务】

    Python 的 asyncio 类似于 C++ 的 Boost.Asio. 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyn ...

  4. 【Python】【有趣的模块】【Requests】无状态 & 无连接

    无状态:原来的Web是静态,后来换成动态的就需要保存一些上下文信息,session和cookie应运而生 无连接:原来为了请求结束后赶紧把资源让出去,后来发现每次请求中有相同的小请求时候再重复执行(而 ...

  5. C++.可变参数_ZC测试

    ZC:环境: Win7 x64(旗舰版),Microsoft Visual Studio 2010(版本 10.0.30319.1 RTMRel, Microsoft .NET Framework(版 ...

  6. Windows 下使用virtualenv 第一次使用flask

    前几天在Windows下virtualenv 了一个名为 venv的目录,然后,今天差点忘了怎么进去虚拟环境. 发现在创建虚拟环境的venv目录下有个Scripts目录,里面有一堆 名为activat ...

  7. 牛客小白月赛7 B 自杀游戏

    自杀游戏 思路: sg函数 代码: #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include&l ...

  8. SQL左右连接中的on and和on where的区别

    SQL左右连接中的on and和on where的区别 左联时,ON后面的对左边表的条件对左边表数据无影响(因为左连接符合左边所有条件),但对右边表数据有影响,只有符合左边表条件时,右边表数据才会查出 ...

  9. 在shell终端操作oracle数据库的常用命令

    这里面是在一个项目中用到的操作oracle数据库的常用linux命令,因为当时无法用plsql远程连接,大部分操作都需要在命令行窗口进行,总结一下 第一种方式 (1)先切换至sqlplus [orac ...

  10. (转)C# Delegate.Invoke、Delegate.BeginInvoke

    Delegate的Invoke.BeginInvoke 1.Delegate.Invoke (委托同步调用) a.委托的Invoke方法,在当前线程中执行委托. b.委托执行时阻塞当前线程,知道委托执 ...