[学习笔记]splay
结构体版
\(c[0]\)左儿子,\(c[1]\)右儿子,\(f\)父亲,\(siz\)以\(u\)为根的子树大小,\(val\)为这个点表示的值,\(cnt\)为这个值的个数,\(rt\)为根.
struct Splay{
int c[2],f,siz,cnt,val;
}tr[N];
int rt;
一些简单函数
\(son(u)\)判断\(u\)为左/右儿子.
\(recnt(u)\)重新计算以\(u\)为根的子树大小.
\(ins_p(f,u,c)\)插入\(u\),为\(f\)的\(c\)儿子.
inline bool son(int u){
return tr[tr[u].f].c[1]==u;
}
inline void recnt(int u){
tr[u].siz=tr[u].cnt+tr[tr[u].c[0]].siz+tr[tr[u].c[1]].siz;
}
inline void ins_p(int f,int u,int c){
tr[u].f=f;tr[f].c[c]=u;
}
旋转
这是\(splay\)的核心代码.
以下图为例:
将紫色节点旋转到粉色节点.
将紫色节点取代粉色节点的位置;
如果紫色节点为粉色节点右儿子,紫色节点的左儿子代替紫色节点原位置,粉色节点为紫色节点左儿子;否则紫色节点的右儿子代替紫色节点原位置,粉色节点为紫色节点右儿子.
旋转后:
\(rotate(u)\)就是将\(u\)旋转到它的父亲.
\(splay(u)\)则是将\(u\)旋转到根:
- 如果\(u\)的父亲为根,直接旋转\(u\).
- 如果\(u,fa\)同为左/右儿子,先转\(fa\),再转\(u\).
- 如果\(u,fa\)不同为左/右儿子,转\(u\),再转\(u\).
inline void rotate(int u){
int f=tr[u].f;bool c=son(u);
ins_p(tr[f].f,u,son(f));
ins_p(f,tr[u].c[c^1],c);
ins_p(u,f,c^1);
recnt(f);recnt(u);
if(!tr[u].f) rt=u;
}
inline void splay(int u){
while(tr[u].f){
if(!tr[tr[u].f].f) rotate(u);
else{
if(son(u)==son(tr[u].f)){
rotate(tr[u].f);rotate(u);
}
else{
rotate(u);rotate(u);
}
}
}
}
插入一个值
如果存在,那个点的\(cnt+1\),否则新建一个点,把它转到根.
inline int find(int x){
int u=rt;
while(tr[u].val!=x&&u){
if(x<tr[u].val)
u=tr[u].c[0];
else u=tr[u].c[1];
}
return u;
}
inline void insert(int x){
int u=find(x),f=0;
if(u){
++tr[u].cnt;
while(u){
recnt(u);u=tr[u].f;
}
return;
}
u=rt;
while(u){
f=u;
if(x<tr[u].val)
u=tr[u].c[0];
else u=tr[u].c[1];
}
tr[++cnt].val=x;
tr[cnt].siz=tr[cnt].cnt=1;
if(!f){
rt=cnt;recnt(rt);return;
}
if(x<tr[f].val)
ins_p(f,cnt,0);
else ins_p(f,cnt,1);
splay(cnt);
}
删除一个点
\(near(u,c)\)求出点\(u\)的前驱和后继(即小于它的数中最大的和大于它的数中最小的,\(c=0\):前驱;\(c=1\):后继).
\(splay\)前驱,\(splay\)后继,后继变为根节点,前驱变为根节点的左儿子,\(u\)变为前驱的右子树,删去即可.
inline int near(int u,int c){
if(tr[u].c[c]){
u=tr[u].c[c];c^=1;
while(tr[u].c[c])
u=tr[u].c[c];
return u;
}
while(son(u)==c){
if(u==rt) return 0;
u=tr[u].f;
}
return tr[u].f;
}
inline void clear(int u){
tr[tr[u].f].c[son(u)]=0;
recnt(tr[u].f);
tr[u].c[0]=tr[u].c[1]=tr[u].f=tr[u].siz=tr[u].cnt=tr[u].val=0;
}
inline void del(int u){
int lst=near(u,0),nxt=near(u,1);
if(!lst||!nxt){
splay(u);
if(lst){
rt=tr[u].c[0];
tr[tr[u].c[0]].f=0;
}
else{
rt=tr[u].c[1];
tr[tr[u].c[1]].f=0;
}
return;
}
splay(lst);splay(nxt);
rotate(lst);clear(u);
}
数组版
\(tr[u][0]\)左儿子,\(tr[u][1]\)右儿子,\(fa[u]\)父亲,\(sz[u]\)以\(u\)为根的子树大小,\(val[u]\)为这个点表示的值,\(ct[u]\)为这个值的个数,\(rt\)为根.
int tr[N][2],fa[N],sz[N],ct[N],val[N],rt;
一些简单函数
\(son(u)\)判断\(u\)为左/右儿子.
\(recnt(u)\)重新计算以\(u\)为根的子树大小.
\(ins_p(f,u,c)\)插入\(u\),为\(f\)的\(c\)儿子.
inline bool son(int u){
return tr[fa[u]][1]==u;
}
inline void recnt(int u){
sz[u]=ct[u]+sz[tr[u][0]]+sz[tr[u][1]];
}
inline void ins_p(int f,int u,int c){
fa[u]=f;tr[f][c]=u;
}
旋转
这是\(splay\)的核心代码.
以下图为例:
将紫色节点旋转到粉色节点.
将紫色节点取代粉色节点的位置;
如果紫色节点为粉色节点右儿子,紫色节点的左儿子代替紫色节点原位置,粉色节点为紫色节点左儿子;否则紫色节点的右儿子代替紫色节点原位置,粉色节点为紫色节点右儿子.
旋转后:
\(rotate(u)\)就是将\(u\)旋转到它的父亲.
\(splay(u)\)则是将\(u\)旋转到根:
- 如果\(u\)的父亲为根,直接旋转\(u\).
- 如果\(u,fa\)同为左/右儿子,先转\(fa\),再转\(u\).
- 如果\(u,fa\)不同为左/右儿子,转\(u\),再转\(u\).
inline void rotate(int u){
int f=fa[u];bool c=son(u);
ins_p(fa[f],u,son(f));
ins_p(f,tr[u][c^1],c);
ins_p(u,f,c^1);
recnt(f);recnt(u);
if(!fa[u]) rt=u;
}
inline void splay(int u){
while(fa[u]){
if(!fa[fa[u]]) rotate(u);
else if(son(u)==son(fa[u])){
rotate(fa[u]);rotate(u);
}
else{
rotate(u);rotate(u);
}
}
}
插入一个值
如果存在,那个点的\(cnt+1\),否则新建一个点,把它转到根.
inline int find(int x){
int u=rt;
while(x!=val(u)&&u)
if(x<val[u])
u=tr[u][0];
else u=tr[u][1];
return u;
}
inline void insert(int x){
int u=find(x);
if(u){
++ct[u];
while(u){
recnt(u);u=fa[u];
}
return;
}
int f=0;u=rt;
while(u){
f=u;
if(x<val[u])
u=tr[u][0];
else u=tr[u][1];
}
val[++cnt]=x;sz[cnt]=ct[cnt]=1;
if(!f){
rt=cnt;recnt(cnt);return;
}
if(x<val[u]) ins_p(f,cnt,0);
else ins_p(f,cnt,1);
splay(cnt);
}
删除一个点
\(near(u,c)\)求出点\(u\)的前驱和后继(即小于它的数中最大的和大于它的数中最小的,\(c=0\):前驱;\(c=1\):后继).
\(splay\)前驱,\(splay\)后继,后继变为根节点,前驱变为根节点的左儿子,\(u\)变为前驱的右子树,删去即可.
inline int near(int u,int c){
if(tr[u][c]){
u=tr[u][c];c^=1;
while(tr[u][c])
u=tr[u][c];
return u;
}
while(son(u)==c){
if(u==rt) return 0;
u=fa[u];
}
return fa[u];
}
inline void clear(int u){
tr[fa[u]][son(u)]=0;recnt(fa[u]);
tr[u][0]=tr[u][1]=fa[u]=sz[u]=ct[u]=val[u]=0;
}
inline void del(int u){
int lst=near(u,0),nxt=near(u,1);
if(!lst||!nxt){
splay(u);
if(lst){
rt=tr[u][0];fa[tr[u][0]]=0;
}
else{
rt=tr[u][1];fa[tr[u][1]]=0;
}
return;
}
splay(lst);splay(nxt);
rotate(lst);clear(u);
}
2017-03-28 23:12:58