约定:
int ch[maxd][3];//0左儿子,1右儿子
int val[maxd];//权值
int rnd[maxd];//随机权值
int sze[maxd];//以每个点为根树的大小
int T,rt,x,y,z;
分裂:
inline void update(int x)
{
sze[x]=1+sze[ch[x][0]]+sze[ch[x][1]];
}
//x为权值小于k的子树的根,y为剩下的子树的根
void split(int now,int k,int &x,int &y)//权值分裂
{
if(!now)
{
x=y=0;
return ;
}
else if(val[now]<=k)//它及它的左子树都小于等于k,递归向右找第二颗树
{
x=now;
split(ch[now][1],k,ch[now][1],y);
}
else
{
y=now;
split(ch[now][0],k,x,ch[now][0]);
}
update(now);//更新sze
}
//x为前k个的子树的根,y为剩下的子树的根
void split1(int now,int k,int &x,int &y)//单点
{
if(!now)
{
x=y=0;
}
else{
if(k<=sze[ch[now][0]])//左子树元素个数大于k个,将右子树全部放在第二颗树上,递归分裂左子树
{
y=now;
split1(ch[now][0],k,x,ch[now][0]);
}
else{
x=now;
split1(ch[now][1],k-sze[ch[now][0]]-1,ch[now][1],y);
}
}
update(now);
}
合并:
//我们假设第一棵树的权值小于第二棵树的权值,
//那么我们就比较它们的随机权值。
//如果rnd[l]<rnd[r],我们就保留它的左子树,
//另一棵树作为它的右子树;如果rnd[l]>=rnd[r],
//那我们可以保留第二棵树的右子树,另一颗树作为它的左子树。
int merge(int A,int B)
{
if(!A||!B)
{
return A+B;
}
if(rnd[A]<rnd[B])
{
ch[A][1]=merge(ch[A][1],B);
update(A);
return A;
}
else
{
ch[B][0]=merge(A,ch[B][0]);
update(B);
return B;
}
}
插入:
int new_node(int x)
{
sze[++T]=1;
val[T]=x;
rnd[T]=rand();
return T;
}
void Inser(int k)
{
split(rt,k,x,y);
rt=merge(merge(x,new_node(k)),y);
}
删除:
//删除权值为v的点,先把整颗树以v为权值split成两棵树a,b,再把a树按照v-1分成c,d。
//这时候值为v的点一定为d的根,
//那么我们把d的两个子儿子merge起来(划重点:这一步就是去除掉v的影响),
//再把他们重新merge起来得到一个新的树,这颗树就去除掉了v的影响。
void del(int k){
split(rt,k,x,z);
split(x,k-1,x,y);
y=merge(ch[y][0],ch[y][1]);
rt=merge(merge(x,y),z);
}
求权值为k的点的排名/排名为k的点的权值:
//直接按照a-1的权值把树分开,那么x树中最大的应该小于等于a-1,那么a的排名就是size[x]+1。
int QueryRank(int k)
{
split(rt,k-1,x,y);
int res=sze[x]+1;
rt=merge(x,y);
return res;
}
int QueryK(int now,int k)
{
while(1)
{
if(k<=sze[ch[now][0]])now=ch[now][0];
else if(k==sze[ch[now][0]]+1) return now;
else k-=sze[ch[now][0]]+1,now=ch[now][1];
}
return 0;
}
求前驱后继:
int pre(int k)
{
split(rt,k-1,x,y);
int res=val[QueryK(x,sze[x])];
rt=merge(x,y);
return res;
}
int suf(int k)
{
split(rt,k,x,y);
int res=val[QueryK(y,1)];
rt=merge(x,y);
return res;
}
普通的平衡树代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
const int maxd = 5e5+10;
inline int read()
{
int x=0,f=1;char c = getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int ch[maxd][3];//0左儿子,1右儿子
int val[maxd];//权值
int rnd[maxd];//随机权值
int sze[maxd];//以每个点为根树的大小
int T,rt,x,y,z;
inline void update(int x)
{
sze[x]=1+sze[ch[x][0]]+sze[ch[x][1]];
}
//x为权值小于k的子树的根,y为剩下的子树的根
void split(int now,int k,int &x,int &y)//权值分裂
{
if(!now)
{
x=y=0;
return ;
}
else if(val[now]<=k)//它及它的左子树都小于等于k,递归向右找第二颗树
{
x=now;
split(ch[now][1],k,ch[now][1],y);
}
else
{
y=now;
split(ch[now][0],k,x,ch[now][0]);
}
update(now);//更新sze
}
//x为前k个的子树的根,y为剩下的子树的根
void split1(int now,int k,int &x,int &y)//单点
{
if(!now)
{
x=y=0;
}
else{
if(k<=sze[ch[now][0]])//左子树元素个数大于k个,将右子树全部放在第二颗树上,递归分裂左子树
{
y=now;
split1(ch[now][0],k,x,ch[now][0]);
}
else{
x=now;
split1(ch[now][1],k-sze[ch[now][0]]-1,ch[now][1],y);
}
}
update(now);
}
//我们假设第一棵树的权值小于第二棵树的权值,
//那么我们就比较它们的随机权值。
//如果rnd[l]<rnd[r],我们就保留它的左子树,
//另一棵树作为它的右子树;如果rnd[l]>=rnd[r],
//那我们可以保留第二棵树的右子树,另一颗树作为它的左子树。
int merge(int A,int B)
{
if(!A||!B)
{
return A+B;
}
if(rnd[A]<rnd[B])
{
ch[A][1]=merge(ch[A][1],B);
update(A);
return A;
}
else
{
ch[B][0]=merge(A,ch[B][0]);
update(B);
return B;
}
}
int new_node(int x)
{
sze[++T]=1;
val[T]=x;
rnd[T]=rand();
return T;
}
void Inser(int k)
{
split(rt,k,x,y);
rt=merge(merge(x,new_node(k)),y);
}
//删除权值为v的点,先把整颗树以v为权值split成两棵树a,b,再把a树按照v-1分成c,d。
//这时候值为v的点一定为d的根,
//那么我们把d的两个子儿子merge起来(划重点:这一步就是去除掉v的影响),
//再把他们重新merge起来得到一个新的树,这颗树就去除掉了v的影响。
void del(int k){
split(rt,k,x,z);
split(x,k-1,x,y);
y=merge(ch[y][0],ch[y][1]);
rt=merge(merge(x,y),z);
}
//直接按照a-1的权值把树分开,那么x树中最大的应该小于等于a-1,那么a的排名就是size[x]+1。
int QueryRank(int k)
{
split(rt,k-1,x,y);
int res=sze[x]+1;
rt=merge(x,y);
return res;
}
int QueryK(int now,int k)
{
while(1)
{
if(k<=sze[ch[now][0]])now=ch[now][0];
else if(k==sze[ch[now][0]]+1) return now;
else k-=sze[ch[now][0]]+1,now=ch[now][1];
}
return 0;
}
int pre(int k)
{
split(rt,k-1,x,y);
int res=val[QueryK(x,sze[x])];
rt=merge(x,y);
return res;
}
int suf(int k)
{
split(rt,k,x,y);
int res=val[QueryK(y,1)];
rt=merge(x,y);
return res;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int op,x;
scanf("%d%d",&op,&x);
switch (op)
{
case 1:
Inser(x);
break;
case 2:
del(x);
break;
case 3:
printf("%d\n",QueryRank(x));
break;
case 4:
printf("%d\n",val[QueryK(rt,x)]);
break;
case 5:
printf("%d\n",pre(x));
break;
case 6:
printf("%d\n",suf(x));
break;
}
}
return 0;
}
区间操作(文艺的平衡树):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
int rt,cnt,x,y,z,n,m;
const int maxn=1e5+10;
int ch[maxn][2];
int rnd[maxn];
int val[maxn];
int f[maxn];
int siz[maxn];
void pushdown(int x)
{
swap(ch[x][0],ch[x][1]);
if(ch[x][0]) f[ch[x][0]]^=1;
if(ch[x][1]) f[ch[x][1]]^=1;
f[x]=0;
}
void pushup(int x)
{
siz[x]=siz[ch[x][1]]+siz[ch[x][0]]+1;
}
void split(int now,int k,int &x,int &y)
{
if(!now)
{
x=y=0;
return ;
}
if(f[now]) pushdown(now);
if(siz[ch[now][0]]<k)
{
x=now;
split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);
}
else
{
y=now;
split(ch[now][0],k,x,ch[now][0]);
}
pushup(now);
}
int merge(int a,int b)
{
if(!a||!b)
{
return a+b;
}
if(rnd[a]<rnd[b])
{
if(f[a]) pushdown(a);
ch[a][1]=merge(ch[a][1],b);
pushup(a);
return a;
}
else
{
if(f[b]) pushdown(b);
ch[b][0]=merge(a,ch[b][0]);
pushup(b);
return b;
}
}
int new_node(int x)
{
val[++cnt]=x;
rnd[cnt]=rand();
siz[x]=1;
return cnt;
}
void Insert(int k)
{
split(rt,k,x,y);
rt=merge(merge(x,new_node(k)),y);
}
void print(int k)
{
if(!k) return ;
if(f[k])pushdown(k);
print(ch[k][0]);
cout<<val[k]<<" ";
print(ch[k][1]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
//int x;
//scanf("%d",&x);
rt=merge(rt,new_node(i));
}
int l,r;
while(m--)
{
scanf("%d%d",&l,&r);
split(rt,l-1,x,y);
split(y,r-l+1,y,z);
f[y]=1;
rt=merge(x,merge(y,z));
}
print(rt);
return 0;
}