splay

匿名 (未验证) 提交于 2019-12-02 23:51:01

(又是一个被我咕咕咕很久了的知识点)

板子题

一、Splay

又叫伸展树,分裂树。是一种二叉排序/查找/搜索树。

Splay是一种平衡二叉树,及优化后的二叉查找树。靠伸展操作splay可以自我调整,使得提升效率

二、二叉排序树的性质

或者是一颗空树

或者具有以下性质:

  • 若左子树不空,则左子树上所有节点的值均小于或等于它的根节点的值
  • 若柚子树不空,则右子树上所有节点的值均大于或等于它的根节点的值
  • 左右子树也分别为二叉排序树

同样的序列,因为排序不同,可能会生成不同的二叉排序树,查找效率性就不一定了。如果傻叉排序树退化成一条链,效率就很低。

三、变量定义

:

: val[ x ]代表x存储的值

: cnt[ x ]代表x存储的重复权值的个数

: fa[ x ]代表x的父节点

: siz[ x ]代表x子树下的储存的权值数(包括重复权值,也包括自己)

四、支持的操作

查找第k大的数

查找一个数的排名

查找一个数的前驱

查找一个数的后继

区间翻转

---------------------------------------------------------------------------------

chk:查询一个节点是其父节点的左儿子还是右儿子

pushup:更新siz数组的值

rotate:旋转保持平衡

splay:伸展,将一个节点一直rotate到制定节点的儿子

find:将最大的小于等于给定x的数所在的节点splay到根

insert:插入

kth:查询第k大的数

rank:查找一个数的排名

pre:查找一个数的前驱

succ:查找一个数的后继

remove:删除一个数

reserve:区间翻转

pushdown:将区间标记下传

#include<cstdio> #include<algorithm> using namespace std;  inline int read() {     int sum = 0,p = 1;     char ch = getchar();     while(ch < '0' || ch > '9')     {         if(ch == '-')             p = -1;         ch = getchar();     }     while(ch >= '0' && ch <= '9')     {         (sum *= 10) += ch - '0';         ch = getchar();     }     return sum * p; }  const int maxn = 1e5 + 10; int n,m,root,ncnt; int ch[maxn][2],fa[maxn],val[maxn],cnt[maxn],siz[maxn],lazy[maxn]; //ch[][0]左儿子,ch[][1]右儿子,fa[]父节点,val[]节点的权值,cnt[]本节点贮存的大小,siz[]子树大小(包括本节点的大小),lazy[]翻转标记  bool chk(int o)//判断一个节点是他父节点的左儿子还是右儿子 {     return ch[fa[o]][1] == o; }  //子树大小重新计算 void pushup(int o) {     siz[o] = siz[ch[o][0]] + siz[ch[o][1]] + cnt[o]; }  //标记下传 void pushdown(int o) {     if(lazy[o])     {         swap(ch[o][0],ch[o][1]);         lazy[ch[o][0]] ^= 1;         lazy[ch[o][1]] ^= 1;         lazy[o] = 0;     } }  //旋转 void rotate(int x) {     int y = fa[x],z = fa[y];     int opt = chk(x),w = ch[x][opt ^ 1];     ch[y][opt] = w;     fa[w] = y;     ch[z][chk(y)] = x;     fa[x] = z;     ch[x][opt ^ 1] = y;     fa[y] = x;     pushup(y),pushup(x); }  //伸展 void  splay(int x,int goal = 0) {     while(fa[x] != goal)     {         int y = fa[x],z = fa[y];         if(z != goal)         {             if(chk(x) == chk(y))                 rotate(y);             else                 rotate(x);         }         rotate(x);     }     if(!goal)         root = x; }  //插入 void insert(int o) {     int cur = root,p = 0;     while(cur && val[cur] != o)     {         p = cur;         cur = ch[cur][o > val[cur]];     }     if(cur)     {         cnt[cur]++;     }     else     {         cur = ++ncnt;         if(p)             ch[p][o > val[p]] = cur;         ch[cur][0] = ch[cur][1] = 0;         fa[cur] = p;         val[cur] = o;         cnt[cur] = siz[cur] = 1;     }     splay(cur); }  //找到权值为o的节点,并放到根节点 void find(int o) {     int cur = root;     while(ch[cur][o > val[cur]] && val[cur] != o)     {         cur = ch[cur][o > val[cur]];     }     splay(cur); }  //求第o大的数 int kth(int o) {     int cur = root;     while(1)     {         pushdown(cur);         if(ch[cur][0] && o <= siz[ch[cur][0]])             cur = ch[cur][0];         else if(o > (siz[ch[cur][0]] + cnt[cur]))         {             o -= (siz[ch[cur][0]] + cnt[cur]);             cur = ch[cur][1];         }         else             return cur;     } }  //翻转 void reverse(int l,int r) {     int x = kth(l),y = kth(r + 2);     splay(x);     splay(y,x);     lazy[ch[y][0]] ^= 1; }  //求前驱 int pre(int o) {     find(o);     if(val[root] < o)         return root;     int cur = ch[root][0];     while(ch[cur][1])         cur = ch[cur][1];     return cur; }  //求后继 int succ(int x) {     find(x);     if(val[root] > x)         return root;     int cur = ch[root][1];     while(ch[cur][0])         cur = ch[cur][0];     return cur; }  //删除 void remove(int o) {     int last = pre(o),nxt = succ(o);     splay(last);     splay(nxt,last);     int del = ch[nxt][0];     if(cnt[del] > 1)     {         cnt[del]--;         splay(del);     }     else         ch[nxt][0] = 0; }   //查询rank void rank(int o) {     find(o);     printf("%d\n",siz[ch[root][0]]); }   //输出 void output(int x) {     pushdown(x);     if(ch[x][0])         output(ch[x][0]);     if(val[x] && val[x] <= n)         printf("%d ",val[x]);     if(ch[x][1])         output(ch[x][1]); }  int main() {     n = read(),m = read();     for(int i = 0; i<= n + 1; i++)         insert(i);     while(m--)     {         int x = read(),y = read();         reverse(x,y);     }     output(root); }
View Code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!