题解 P3369 【【模板】普通平衡树(Treap/SBT)】
Great_Influence · · 题解
来一发
当你写二叉平衡树的时候,因为每个节点的儿子数不同导致讨论冗杂,容易出错。并且常用的几个数据结构中,
你为什么不问问神奇海螺呢?
答案就是
同理,对于双旋,有:(图见下方)
此时考虑对于平衡因子
至于维护的方法,就是当发现树失衡时,根据较小树的平衡度
我的代码中有一个
代码:
inline bool isr(int x){return x==son[fa[x]][1];}
inline void rotate(int x)//简单平衡树的旋转操作(一模一样)
{
static int f,ff,k;f=fa[x];ff=fa[f];k=isr(x);
son[fa[son[x][k^1]]=f][k]=son[x][k^1];
son[fa[x]=ff][isr(f)]=x;
son[fa[f]=x][k^1]=f;
refresh(f);refresh(x);
if(f==rt)rt=x;
}
const double alp=1-sqrt(2)/2,lim=(1-2*alp)/(1-alp);
inline void maintain(int x)//保持平衡
{
static int dir;
if(son[x][0])//非叶子节点才需要维护平衡
{
if(sz[son[x][0]]<sz[x]*alp)dir=1;//找到较小的一个子树
else if(sz[son[x][1]]<sz[x]*alp)dir=0;
else return;//没有失衡则跳出
if(sz[son[son[x][dir]][dir^1]]>=sz[son[x][dir]]*lim)//分情况分类讨论
rotate(son[son[x][dir]][dir^1]);
rotate(son[x][dir]);
}
}
void insert(int now,int x)
{
if(!rt){rt=newnode(x);return;}//特判根节点
if(sz[now]==1)//找到叶子结点
{
fa[son[now][0]=newnode(x)]=now;//开启新节点,当前叶子结点下放
fa[son[now][1]=newnode(ma[now])]=now;
if(x>ma[now])swap(son[now][0],son[now][1]);
}
else insert(son[now][x>ma[son[now][0]]],x);//向下递归
refresh(now);//维护
maintain(now);
}
2.删除
发现因为节点只会删去叶子结点,所以只需要将结点直接删去,其父亲叶子结点由另一棵子树代替,也被删除。
代码:
void del(int now,int x)
{
int dir=x>ma[son[now][0]],t;
if(sz[son[now][dir]]==1)//找到叶子结点
{
delet(son[now][dir]);//直接删掉
fa[t=son[now][dir^1]]=fa[now];//用另一棵子树替代辅助节点并删除
son[fa[now]][isr(now)]=t;
delet(now);
if(now==rt)rt=t;//特判根的情况
now=t;
}
else del(son[now][dir],x);//向下递归
refresh(now);//维护
maintain(now);
}
3.求结点rank
这个和普通平衡树基本一样。但是因为每个节点的
代码:
int find_by_order(int now,int x)
{
if(sz[now]==1)return now;//找到叶子结点即返回
if(sz[son[now][0]]>=x)return find_by_order(son[now][0],x);//递归查找
else return find_by_order(son[now][1],x-sz[son[now][0]]);
}
4.找到第k大元素
和普通平衡树差不多。同样也少了很多细节。
代码:
int order_of_key(int now,int x)
{
if(x<=mi[now])return 0;//数字不在范围内则返回0
if(sz[now]==1)return 1;//找到叶子返回1
if(ma[son[now][0]]>=x)return order_of_key(son[now][0],x);//递归求解
else return sz[son[now][0]]+order_of_key(son[now][1],x);
}
5.查找前驱/后继
直接按照定义来查找就可以了。
代码:
int pre(int now,int x)
{
if(sz[now]==1)return now;
if(mi[son[now][1]]>=x)return pre(son[now][0],x);
else return pre(son[now][1],x);
}
int suf(int now,int x)
{
if(sz[now]==1)return now;
if(ma[son[now][0]]>x)return suf(son[now][0],x);
else return suf(son[now][1],x);
}
经过测试,本程序速度优秀。在自带大常数情况下,在我自己打的几种不同平衡树中,仅次于
整体代码:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define Rep(i,a,b) for(register int i=(a),i##end=(b);i<=i##end;++i)
#define Repe(i,a,b) for(register int i=(a),i##end=(b);i>=i##end;--i)
#define For(i,a,b) for(i=(a),i<=(b);++i)
#define Forward(i,a,b) for(i=(a),i>=(b);--i)
#define Chkmin(a,b) a=a<b?a:b
#define Chkmax(a,b) a=a>b?a:b
#define pb push_back
inline void read(int &x)
{
static const int BUFSIZE = 1048576;
static char buf[BUFSIZE];
static char *bufnow = buf;
static char *bufmax = buf;
if (bufnow == bufmax) {
bufmax = buf + fread(buf, 1, BUFSIZE, stdin);
bufnow = buf;
}
static int c,f;
f=1;
c = *bufnow++;
for (;!isdigit(c)&&(c^'-');c = *bufnow++) {
if (bufnow == bufmax) {
bufmax = buf + fread(buf, 1, BUFSIZE, stdin);
bufnow = buf;
}
}
if(!isdigit(c)){f=-1;c=*bufnow++;}
x = 0;
for (;isdigit(c);c = *bufnow++) {
x = (x << 1) + (x << 3) + c - 48;
if (bufnow == bufmax) {
bufmax = buf + fread(buf, 1, BUFSIZE, stdin);
bufnow = buf;
}
}
x*=f;
}
inline void write(int a,char ed='\n')
{
static short s[13],tp;
if(!a){putchar('0'),putchar(ed);return;}
for(tp=0;a;a/=10)s[++tp]=a%10;
for(;tp;putchar(s[tp--]^48));
putchar(ed);
}
using namespace std;
const int MAXN=2e5+27;
static int n;
namespace leafy_tree
{
const double alp=1.0-sqrt(2)/2,lim=(1-2*alp)/(1-alp);
static int sz[MAXN],fa[MAXN],son[MAXN][2],ma[MAXN],mi[MAXN],rt;
inline void refresh(int h)
{
if(son[h][0])
{
mi[h]=mi[son[h][0]];ma[h]=ma[son[h][1]];
sz[h]=sz[son[h][0]]+sz[son[h][1]];
}
}
inline bool isr(int x){return x==son[fa[x]][1];}
inline void rotate(int x)
{
static int f,ff,k;f=fa[x];ff=fa[f];k=isr(x);
son[fa[son[x][k^1]]=f][k]=son[x][k^1];
son[fa[x]=ff][isr(f)]=x;
son[fa[f]=x][k^1]=f;
refresh(f);refresh(x);
if(f==rt)rt=x;
}
inline void maintain(int x)
{
static int dir;
if(son[x][0])
{
if(sz[son[x][0]]<sz[x]*alp)dir=1;
else if(sz[son[x][1]]<sz[x]*alp)dir=0;
else return;
if(sz[son[son[x][dir]][dir^1]]>=sz[son[x][dir]]*lim)
rotate(son[son[x][dir]][dir^1]);
rotate(son[x][dir]);
}
}
static int sta[MAXN],tp;
inline int newnode(int x)
{
ma[sta[tp]]=mi[sta[tp]]=x;sz[sta[tp]]=1;
return sta[tp--];
}
void insert(int now,int x)
{
if(!rt){rt=newnode(x);return;}
if(sz[now]==1)
{
fa[son[now][0]=newnode(x)]=now;
fa[son[now][1]=newnode(ma[now])]=now;
if(x>ma[now])swap(son[now][0],son[now][1]);
}
else insert(son[now][x>ma[son[now][0]]],x);
refresh(now);
maintain(now);
}
inline void delet(int x)
{son[x][0]=son[x][1]=fa[x]=sz[x]=0;sta[++tp]=x;}
void del(int now,int x)
{
int dir=x>ma[son[now][0]],t;
if(sz[son[now][dir]]==1)
{
delet(son[now][dir]);
fa[t=son[now][dir^1]]=fa[now];
son[fa[now]][isr(now)]=t;
delet(now);
if(now==rt)rt=t;
now=t;
}
else del(son[now][dir],x);
refresh(now);
maintain(now);
}
int find_by_order(int now,int x)
{
if(sz[now]==1)return now;
if(sz[son[now][0]]>=x)return find_by_order(son[now][0],x);
else return find_by_order(son[now][1],x-sz[son[now][0]]);
}
int order_of_key(int now,int x)
{
if(x<=mi[now])return 0;
if(sz[now]==1)return 1;
if(ma[son[now][0]]>=x)return order_of_key(son[now][0],x);
else return sz[son[now][0]]+order_of_key(son[now][1],x);
}
int pre(int now,int x)
{
if(sz[now]==1)return now;
if(mi[son[now][1]]>=x)return pre(son[now][0],x);
else return pre(son[now][1],x);
}
int suf(int now,int x)
{
if(sz[now]==1)return now;
if(ma[son[now][0]]>x)return suf(son[now][0],x);
else return suf(son[now][1],x);
}
}
using namespace leafy_tree;
inline void work()
{
read(n);
Rep(i,1,(n<<1)+10)sta[i]=i;tp=(n<<1)+10;
static int opt,x;
insert(rt,-2147483647);
insert(rt,2147483647);
Rep(i,1,n)
{
read(opt);read(x);
switch(opt)
{
case 1:insert(rt,x);break;
case 2:del(rt,x);break;
case 3:printf("%d\n",order_of_key(rt,x));break;
case 4:printf("%d\n",ma[find_by_order(rt,x+1)]);break;
case 5:printf("%d\n",ma[pre(rt,x)]);break;
case 6:printf("%d\n",ma[suf(rt,x)]);break;
}
}
}
inline void file()
{
#ifndef ONLINE_JUDGE
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
#endif
}
int main()
{
file();
work();
cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
return 0;
}