标签:line 核心 文章 ide ace return 为什么 代码 reg
由于我怕学习了Splay之后不直接写blog第二天就忘了,所以强行加了一波优先级。
论谁是天下最秀平衡树,我Splay第一个不服。维护平衡只靠旋转。
一言不合转死你
由于平衡树我也介绍了两种Treap&&Scapegoat Tree,所以一些互通的东西也不讲了。
这次的亮点主要是为了弥补Splay的巨大常数(据说是96),我把大量的函数都写成了迭代版本。
废话不多说开讲。
关于旋转操作,在Treap的那篇文章中已经讲的比较详细了。
但是注意一下Splay的旋转不同于Treap,一般Splay旋转时简单的理解就是儿子翻身当爸爸
什么意思,其实像这样:
\(x,y\)的关系还是很好理解的,主要是\(x\)的某个儿子被旋到哪里去了旋的头晕
我们再手玩几种情况:
\(y\)是\(z\)的左儿子 \(x\)是\(y\)的左儿子
\(y\)是\(z\)的左儿子 \(x\)是\(y\)的右儿子
\(y\)是\(z\)的右儿子 \(x\)是\(y\)的右儿子
疯狂手玩ing
那我们来总结一波性质吧:
然后我们就可以搞出旋转的核心代码了(具体看下面)
为什么Splay要叫Splay呢。请自行使用词典查询Splay的意思。好了我懂了
一个节点已经不满足屈膝于它人的儿子了,我就是想俯视众生
正常的说,就是想要当根节点。真有梦想
那么让你飞,但是我只需要无脑暴力上旋就可以维护平衡这一重要性质了吗?
我将一个\(x\)进行上旋操作,像这样:
不是说好了要当根的吗,那我把\(x\)再旋转一波:
然后我们很意外的发现,红色的那一条链一点都没有改变
也就意味这数据还是可以把你给卡掉
然后我们发现,对于\(x,y,z\)的不同位置情况,我们要分别进行讨论
然后蒟蒻就开始\(2^3=8\)中情况大力讨论
手玩了好久好久。。。。。。
其实简化了之后也就两种:
于是我们就可以简化一波操作,就完成了Splay的核心内容。
其它的一些操作都是具有BST性质的了,就是主要一点:
有事没事Splay一下要不然干嘛叫Splay
还是板子题:Luogu P3369 【模板】普通平衡树的CODE(跑的并不慢)
#include<cstdio>
#include<cctype>
#define lc(x) (node[x].ch[0])
#define rc(x) (node[x].ch[1])
#define fa(x) (node[x].fa)
#define tc() (A==B&&(B=(A=fl)+fread(fl,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (top<S?st[top++]=ch:(fwrite(st,1,S,stdout),st[(top=0)++]=ch))
using namespace std;
const int N=100005,S=1<<20,INF=2147483647;
char fl[S],st[S],*A=fl,*B=fl;
struct Splay
{
int ch[2],size,cnt,fa,val;
}node[N];
int n,opt,x,rt,tot,top;
inline void read(int &x)
{
x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^‘-‘?1:-1;
while (x=(x<<3)+(x<<1)+ch-‘0‘,isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{
if (x<0) pc(‘-‘),x=-x;
if (x>9) write(x/10); pc(x%10+‘0‘);
}
inline void pushup(int now)
{
node[now].size=node[lc(now)].size+node[rc(now)].size+node[now].cnt;
}
inline int build(int val,int fa)
{
node[++tot].val=val; node[tot].size=node[tot].cnt=1; fa(tot)=fa;
lc(tot)=rc(tot)=0; if (fa) node[fa].ch[val>node[fa].val]=tot; return tot;
}
inline int identify(int now)
{
return node[fa(now)].ch[1]==now;
}
inline void connect(int son,int fa,int d)
{
fa(son)=fa; node[fa].ch[d]=son;
}
inline void rotate(int now)
{
int x=node[now].fa,y=node[x].fa,dx=identify(now),dy=identify(x),son=node[now].ch[dx^1];
connect(son,x,dx); connect(x,now,dx^1); connect(now,y,dy); pushup(x); pushup(now);
}
inline void splay(int now,int tar)
{
while (fa(now)!=tar)
{
if (fa(fa(now))!=tar)
{
if (identify(now)^identify(fa(now))) rotate(now); else rotate(fa(now));
} rotate(now);
}
if (!tar) rt=now;
}
inline void get_rank(int val)
{
int now=rt; if (!now) return;
while (node[now].val!=val&&node[now].ch[val>node[now].val])
now=node[now].ch[val>node[now].val]; splay(now,0);
}
inline int get_val(int rk)
{
int now=rt;
for (;;)
{
if (rk>node[lc(now)].size+node[now].cnt) rk-=node[lc(now)].size+node[now].cnt,now=rc(now);
else if (node[lc(now)].size>=rk) now=lc(now); else return node[now].val;
}
}
inline int next(int val,int d)
{
get_rank(val); int now=rt;
if ((node[now].val>val&&d)||(node[now].val<val&&!d)) return now;
now=node[now].ch[d]; while (node[now].ch[d^1]) now=node[now].ch[d^1]; return now;
}
inline void insert(int val)
{
int now=rt,fa=0;
while (now&&node[now].val!=val) fa=now,now=node[now].ch[val>node[now].val];
if (!now) now=build(val,fa); else ++node[now].cnt; splay(now,0);
}
inline void remove(int val)
{
int lst=next(val,0),nxt=next(val,1); splay(lst,0); splay(nxt,lst); int del=lc(nxt);
if (node[del].cnt>1) --node[del].cnt,splay(del,0); else lc(nxt)=0;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); insert(-INF); insert(+INF);
for (i=1;i<=n;++i)
{
read(opt); read(x);
switch (opt)
{
case 1:insert(x);break;
case 2:remove(x);break;
case 3:get_rank(x),write(node[lc(rt)].size),pc(‘\n‘);break;
case 4:write(get_val(x+1)),pc(‘\n‘);break;
case 5:write(node[next(x,0)].val),pc(‘\n‘);break;
case 6:write(node[next(x,1)].val),pc(‘\n‘);break;
}
}
return fwrite(st,1,top,stdout),0;
}
标签:line 核心 文章 ide ace return 为什么 代码 reg
原文地址:https://www.cnblogs.com/cjjsb/p/9419820.html