题干:splay模板,要求维护区间反转。
splay是一种码量小于treap,但支持排名,前驱后继等treap可求的东西,也支持区间反转的平衡树。
但是有两个坏处:
1.splay常数远远大于treap以及stl中的set。
2.没有可持久化splay,但有可持久化treap。
下面是代码:
1.pushup以及pushdown
pushup用于维护某点所在子树大小。
void pushup(int u) { tr[u].siz = tr[tr[u].ch[0]].siz + tr[tr[u].ch[1]].siz + 1; }
一行。
pushdown用于标记下推。
void pushdown(int u) { if(tr[u].mrk) { tr[u].mrk=0; swap(tr[u].ch[0],tr[u].ch[1]); tr[tr[u].ch[0]].mrk^=1; tr[tr[u].ch[1]].mrk^=1; } }
2.rotate
rotate相当于treap中的lturn和rturn,是将x点转到他的父节点处。
void rotate(int x) { int y = tr[x].fa; int z = tr[y].fa; int k = (tr[y].ch[1]==x); tr[x].fa = z,tr[z].ch[tr[z].ch[1]==y]=x; tr[y].ch[k] = tr[x].ch[k^1],tr[tr[x].ch[k^1]].fa = y; tr[y].fa = x,tr[x].ch[k^1]=y; pushup(y),pushup(x); }
注意改变父子关系时的顺序问题,还有两个pushup,应该先pushup儿子后pushup父亲。
3.splay(splay核心操作)
splay(a,b),将a旋转到b的儿子处。
下面是双旋splay:
void splay(int x,int goal) { while(tr[x].fa!=goal) { int y = tr[x].fa; int z = tr[y].fa; if(z!=goal) ((tr[z].ch[1]==y)&(tr[y].ch[1]==x))?rotate(x):rotate(y); rotate(x); } if(goal==0)rot=x; }
代码中while中的if,机房普遍认为是为了维护整棵树的深度。
4.insert
码量比treap少很多:
void insert(int x) { int u = rot,fa; while(u)fa=u,u=tr[u].ch[tr[u].v<x]; u=++tot; if(fa)tr[fa].ch[x>tr[fa].v]=u; tr[u].fa = fa,tr[u].v = x,tr[u].siz = 1; splay(u,0); }
5.查询排名
代码:
int query_kth(int k) { int u = rot; while(1) { pushdown(u); int t = tr[tr[u].ch[0]].siz; if(t==k-1)return u; else if(t>=k)u=tr[u].ch[0]; else k-=t+1,u=tr[u].ch[1]; } }
6.区间旋转
先查询前驱后继,然后把所在子树夹到前驱后继之间,一个标记解决问题。
void chg(int l,int r) { l = query_kth(l);//前驱 r = query_kth(r+2);//后继 splay(l,0);splay(r,l); tr[tr[tr[rot].ch[1]].ch[0]].mrk^=1; }
最后:
推极大极小,推极大极小,推极大极小!!!
全代码就不粘了。
原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9592046.html
时间: 2024-11-13 00:26:02