(又是一个被我咕咕咕很久了的知识点)
一、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); }