先了解一下什么是平衡树吧。
之后进行各种玄学操作,但都离不开平衡树的基本性质:
满足根存在一性质,大于左子树的同一性质,小于右子树的同一性质
请记住这句话。
定义结构体(以普通平衡树为例):
struct node{int d,n,c,f,son[2];}t[N];int len,root;//son[0]为左,son[1]为右
先讲讲五个基本操作吧。
操作(也就是一个向上传递的操作)
void update(int p)
{
int l=t[p].son[0],r=t[p].son[1];
t[p].c=t[l].c+t[r].c+1;
}
操作(加点操作)
void add(int d,int f)
{
++len;t[len].d=d;t[len].f=f;t[len].n=t[len].c=1;
t[len].son[0]=t[len].son[1]=0;
d<t[f].d?t[f].son[0]=len:t[f].son[1]=len;//运用性质
}
难点:(其实也不难)
操作:
以右旋为例,我们要让 成为 的孩子。
为了满足平衡树的性质
应变成:
其他情况类似。
void rotate(int p,int w)
{
int f=t[p].f,gf=t[f].f;
int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
r=p;R=gf;t[R].son[0]==f?t[R].son[0]=r:t[R].son[1]=r;t[r].f=R;
r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f);update(p);
}
操作( 即让 成为 的孩子,同时保持平衡树结构)
void splay(int p,int rt)
{
while(t[p].f!=rt)
{
int f=t[p].f,gf=t[f].f;
if(gf==rt)t[f].son[0]==p?rotate(p,1):rotate(p,0);
else
{
if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
else rotate(f,0),rotate(p,0);
}
}
if(!rt)root=p;//让p成为root
}
操作(找最接近 值的点)
从 出发,根据平衡树性质,往下搜索。
int get_id(int d)
{
int p=root;
while(t[p].d!=d)
{
if(d<t[p].d)
{
if(!t[p].son[0])break;
p=t[p].son[0];
}
if(d>t[p].d)
{
if(!t[p].son[1])break;
p=t[p].son[1];
}
}
return p;
}
找到 值的排名(第几小), 操作。
int rk(int d)
{
int p=get_id(d);splay(p,0);
return t[t[p].son[0]].c+1;
}
找到第 小的值, 操作
int rkc(int k)
{
int p=root;
while(1)
{
pushdown(p);
int lc=t[p].son[0],rc=t[p].son[1];
if(k<=t[lc].c)p=lc;
else if(k>t[lc].c+1)k-=t[lc].c+1,p=rc;
else break;
}
return p;
}
找到 值的位置的前驱, 操作
int prv(int d)
{
int p=get_id(d);splay(p,0);
if(d<=t[p].d&&t[p].son[0])
for(p=t[p].son[0];t[p].son[1];p=t[p].son[1]);
if(d<=t[p].d)p=0;//这句话说明找不到前驱
return p;
}
这里需要说明一下,由于 找到的是一个最接近d的值(或大或小,含等于)
(往下潜到左子树(右子树)为空时停止,因此不存在程序找不到前驱,而实际存在前驱的情况。)
有时根据特殊要求,可以排掉等号。
操作类似。
int nxt(int d)
{
int p=get_id(d);splay(p,0);
if(d>=t[p].d&&t[p].son[1])
for(p=t[p].son[1];t[p].son[0];p=t[p].son[0]);
if(d>=t[p].d)p=0;
return p;
}
这就是普通平衡树的全部操作。
AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
x=0;int f=1;char c=gc;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
x*=f;
}
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
struct node{int d,c,n,son[2],f;}t[N];int root,len;
void update(int p){t[p].c=t[p].n+t[t[p].son[0]].c+t[t[p].son[1]].c;}
void add(int d,int f)
{
t[++len]=(node){d,1,1,{0,0},f};
t[f].son[d>t[f].d]=len;
}
void rotate(int p,int w)
{
int f=t[p].f,gf=t[f].f;
int r=t[p].son[w],R=f;t[R].son[w^1]=r;t[r].f=R;
r=p;R=gf;t[R].son[0]==f?t[R].son[0]=r:t[R].son[1]=r;t[r].f=R;
r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f),update(p);
}
void splay(int p,int rt)
{
while(t[p].f!=rt)
{
int f=t[p].f,gf=t[f].f;
if(gf==rt)rotate(p,t[f].son[0]==p);
else
{
if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
else rotate(f,0),rotate(p,0);
}
}
if(!rt)root=p;
}
int get_id(int d)
{
int p=root;
while(t[p].d!=d)
{
if(d<t[p].d)
{
if(!t[p].son[0])break;
p=t[p].son[0];
}
if(d>t[p].d)
{
if(!t[p].son[1])break;
p=t[p].son[1];
}
}
return p;
}
void ins(int d)
{
if(!root){add(d,0);root=len;return;}
int p=get_id(d);
if(t[p].d==d)++t[p].n,update(p),splay(p,0);
else add(d,p),update(p),splay(len,0);
}
void del(int d)
{
int p=get_id(d);splay(p,0);
if(t[p].n>1){--t[p].n;update(p);return ;}
if(!t[p].son[0]&&!t[p].son[1])root=0,len=0;
else if(t[p].son[0]&&!t[p].son[1])root=t[p].son[0],t[root].f=0;
else if(!t[p].son[0]&&t[p].son[1])root=t[p].son[1],t[root].f=0;
else
{
int R=t[p].son[0];while(t[R].son[1])R=t[R].son[1];splay(R,p);
int r=t[p].son[1];t[R].son[1]=r;t[r].f=R;root=R;t[root].f=0;
splay(R,0);
}
}
int rk(int d)
{
int p=get_id(d);splay(p,0);
return t[t[p].son[0]].c+1;
}
int kth(int k)
{
int p=root;
while(233)
{
int lc=t[p].son[0],rc=t[p].son[1];
if(k<=t[lc].c)p=lc;
else if(k>t[lc].c+t[p].n)k-=t[lc].c+t[p].n,p=rc;
else break;
}
return t[p].d;
}
int prv(int d)
{
int p=get_id(d);splay(p,0);
if(d<=t[p].d&&t[p].son[0])//若d<t[p].d,则说明d不存在于平衡树中,t[p].d为在比d大的情况下,最接近d的数
for(p=t[p].son[0];t[p].son[1];p=t[p].son[1]);
if(d<=t[p].d)p=0;//没有左孩子
return t[p].d;
}
int nxt(int d)
{
int p=get_id(d);splay(p,0);
if(d>=t[p].d&&t[p].son[1])
for(p=t[p].son[1];t[p].son[0];p=t[p].son[0]);
if(d>=t[p].d)p=0;//没有右孩子
return t[p].d;
}
int main()
{
int n;qr(n);
for(int i=1;i<=n;i++)
{
int cz,x;qr(cz),qr(x);
if(cz==1)ins(x);
else if(cz==2)del(x);
else if(cz==3)qw(rk(x)),puts("");
else if(cz==4)qw(kth(x)),puts("");
else if(cz==5)qw(prv(x)),puts("");
else qw(nxt(x)),puts("");
}
return 0;
}
关于区间翻转,平衡树同样可以用,
只要满足性质
满足根存在一性质,大于左子树的同一性质,小于右子树的同一性质
这里这个性质也就是每一个数在序列的下标,下标保持二叉搜索树结构,就可以用 进行维护,
经过观察发现,当要翻转的数,处于同一棵子树,且子树中全为要翻转的数时,随意瞎搞都行。
添加两个哨兵节点 , ,翻转操作时,我们需要用到这两个节点去翻转 区间的,注意,平衡树中的下标比序列的下标多 。
如何翻转使得一个区间内的数处于同一棵子树呢?
比如我们需要翻转区间 ,需要使 处于同一棵子树,可以把平衡树中第 小的数(相当于序列中位置 )调到根上,再将第 小的数(相当于序列中位置 ,此时,区间 即为 的左子树,为什么?平衡树的性质啊!
接着我们进行打标记,顺便在翻转时懒惰操作一下。
AC code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
x=0;int f=1;char c=gc;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
x*=f;
}
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
struct node{int d,son[2],f,c;bool v;}t[N];int len,root;
void update(int p){int l=t[p].son[0],r=t[p].son[1];t[p].c=t[l].c+t[r].c+1;}
void build(int &p,int f,int l,int r)
{
if(l>r){p=0;return ;}
int mid=(l+r)>>1;p=++len;t[p].f=f;t[p].d=mid,t[p].c=1;
build(t[p].son[0],p,l,mid-1);
build(t[p].son[1],p,mid+1,r);
update(p);
}
void rotate(int p,int w)
{
int f=t[p].f,gf=t[f].f;
int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
r=p;R=gf;t[R].son[0]==f?t[R].son[0]=r:t[R].son[1]=r;t[r].f=R;
r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f);update(p);
}
void splay(int p,int rt)
{
while(t[p].f!=rt)
{
int f=t[p].f,gf=t[f].f;
if(gf==rt)t[f].son[0]==p?rotate(p,1):rotate(p,0);
else
{
if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
else rotate(f,0),rotate(p,0);
}
}
if(!rt)root=p;
}
int pushdown(int p)
{
if(t[p].v)
{
int &lc=t[p].son[0],&rc=t[p].son[1];
swap(lc,rc);t[p].v=0;t[lc].v^=1;t[rc].v^=1;
}
}
int rkc(int k)
{
int p=root;
while(1)
{
pushdown(p);
int lc=t[p].son[0],rc=t[p].son[1];
if(k<=t[lc].c)p=lc;
else if(k>t[lc].c+1)k-=t[lc].c+1,p=rc;
else break;
}
return p;
}
void work(int l,int r)
{
l=rkc(l),r=rkc(r+2);
splay(l,0),splay(r,l);
t[t[r].son[0]].v^=1;
}
void print(int p)
{
if(!p)return ;
pushdown(p);
print(t[p].son[0]);
if(t[p].d)qw(t[p].d),putchar(' ');
print(t[p].son[1]);
}
int main()
{
int n,m,l,r;qr(n),qr(m);
len=0;build(t[0].son[0],0,0,n+1);root=t[0].son[0];t[len].d=0;
for(int i=1;i<=m;i++){qr(l),qr(r);work(l,r);}
print(root);puts("");
return 0;
}