2803: 普通平衡树
时间限制: 2 Sec 内存限制: 128 MB题目描述
你需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入一个整数x
2. 删除一个整数x(若有多个相同的数,只删除一个)
3. 查询整数x的排名(若有多个相同的数,输出最小的排名),相同的数依次排名,不并列排名
4. 查询排名为x的数,排名的概念同3
5. 求x的前驱(前驱定义为小于x,且最大的数),保证x有前驱
6. 求x的后继(后继定义为大于x,且最小的数),保证x有后继
输入
第一行为n,表示操作的个数(n <= 500000)
下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6, -10^7 <= x <= 10^7)
大规模输入数据,建立读入优化
输出
对于操作3,4,5,6每行输出一个数,表示对应答案
样例输入
8
1 10
1 20
1 30
3 20
4 2
2 10
5 25
6 -1
样例输出
2
20
20
20
平衡树基础操作,上版:
Avl:
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int Max = 500005;
const int INF = 0x3f3f3f3f;
struct node{
int num, sum;
int lc, rc, h;
}Tr[Max];
int cnt, Pre, Suc;
void getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9') if(c == '-') flg = -1;
while(c >= '0' && c <= '9') { num = num * 10 + c - 48; c = getchar();}
num *= flg;
}
int max(int a, int b){ return a < b ? b : a;}
int min(int a, int b){ return a < b ? a : b;}
void pushup(int & r){
Tr[r].sum = Tr[Tr[r].lc].sum + Tr[Tr[r].rc].sum + 1;
Tr[r].h = max(Tr[Tr[r].lc].h, Tr[Tr[r].rc].h) + 1;
}
int zig(int r){
int t = Tr[r].lc;
Tr[r].lc = Tr[t].rc, Tr[t].rc = r;
pushup(r), pushup(t);
return t;
}
int zag(int r){
int t = Tr[r].rc;
Tr[r].rc = Tr[t].lc, Tr[t].lc = r;
pushup(r), pushup(t);
return t;
}
int zigzag(int r){
Tr[r].rc = zig(Tr[r].rc);
return zag(r);
}
int zagzig(int r){
Tr[r].lc = zag(Tr[r].lc);
return zig(r);
}
void maintain(int & r){
if(Tr[Tr[r].lc].h == Tr[Tr[r].rc].h + 2){
int t = Tr[r].lc;
if(Tr[Tr[t].lc].h == Tr[Tr[r].rc].h + 1) r = zig(r);
else if(Tr[Tr[t].rc].h == Tr[Tr[r].rc].h + 1) r = zagzig(r);
}
else if(Tr[Tr[r].rc].h == Tr[Tr[r].lc].h + 2){
int t = Tr[r].rc;
if(Tr[Tr[t].rc].h == Tr[Tr[r].lc].h + 1) r = zag(r);
else if(Tr[Tr[t].lc].h == Tr[Tr[r].lc].h + 1) r = zigzag(r);
}
pushup(r);
}
void Insert(int & r, int val){
if(! r){
r = ++ cnt;
Tr[r].num = val;
Tr[r].h = Tr[r].sum = 1;
return ;
}
if(val < Tr[r].num) Insert(Tr[r].lc, val);
else Insert(Tr[r].rc, val);
maintain(r);
}
int Dele(int & r, int val){
int tx;
if(Tr[r].num == val || (val < Tr[r].num && ! Tr[r].lc) || ( val > Tr[r].num && ! Tr[r].rc)){
if(! Tr[r].lc || ! Tr[r].rc){
tx = Tr[r].num;
r = Tr[r].lc + Tr[r].rc;
return tx;
}
else Tr[r].num = Dele(Tr[r].lc, val);
}
else {
if(val < Tr[r].num) tx = Dele(Tr[r].lc, val);
else tx = Dele(Tr[r].rc, val);
}
maintain(r);
return tx;
}
int Srank(int r, int val){
if(! r) return 0;
if(val <= Tr[r].num) return Srank(Tr[r].lc, val);
else return Tr[Tr[r].lc].sum + 1 + Srank(Tr[r].rc, val);
}
int Frank(int r, int val){
if(Tr[Tr[r].lc].sum + 1 == val) return Tr[r].num;
if(Tr[Tr[r].lc].sum + 1 < val) return Frank(Tr[r].rc, val - Tr[Tr[r].lc].sum - 1);
else return Frank(Tr[r].lc, val);
}
int Bef(int r, int val){
int root = r, rt = -INF;
while(root){
if(Tr[root].num == val) root = Tr[root].lc;
else if(Tr[root].num < val)
rt = max(rt, Tr[root].num), root = Tr[root].rc;
else root = Tr[root].lc;
}
return rt;
}
int Aft(int r, int val){
int root = r, rt = INF;
while(root){
if(Tr[root].num == val) root = Tr[root].rc;
else if(Tr[root].num > val)
rt = min(rt, Tr[root].num), root = Tr[root].lc;
else root = Tr[root].rc;
}
return rt;
}
int main(){
int T, ord, x, R = 0;
getint(T);
while(T --){
getint(ord), getint(x);
switch(ord){
case 1:{ Insert(R, x); break;}
case 2:{ Dele(R, x); break;}
case 3:{ printf("%d\n", Srank(R, x) + 1);break;}
case 4:{ printf("%d\n", Frank(R, x)); break;}
case 5:{ Pre = Bef(R, x), printf("%d\n", Pre); break;}
case 6:{ Suc = Aft(R, x), printf("%d\n", Suc); break;}
}
}
return 0;
}
Splay:这是很多年前的一个版了,也一直懒,就没重写,下午来看会不会重写
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=500555;
const int inf=0x7fffffff;
int root,n,opt,tot=0;//root和tot都定义为0.
int k[maxn],cnt[maxn],sz[maxn],fa[maxn],ch[maxn][2];
void update(int x){
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void getint(int&num) {
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
void rotato(int x) { //只需一个参数。六次赋值。其中有孙子和祖父加上判断
int y=fa[x],z=fa[y],flg=(ch[y][1]==x);
ch[y][flg]=ch[x][!flg];
if(ch[y][flg])fa[ch[y][flg]]=y;
ch[x][!flg]=y,fa[y]=x;
fa[x]=z;
if(z)ch[z][ch[z][1]==y]=x;
update(y); //此处更新y即可。update(x)可省略。
}
void splay(int x,int goal){//goal可以控制splay到哪里结束。
for(int y;(y=fa[x])!=goal;rotato(x))
{
int z=fa[y];
if(z!=goal)
{
if((ch[z][0]==y)==(ch[y][0]==x))
rotato(y);
else
rotato(x);
}
}
if(goal==0)
root=x;//更新root,但是要加上条件。
update(x);//必不可少。存在特殊情况,x不需要旋转,但其sz被修改。
}
void insert(int val){
int x=root,flag=0,y=0;
while(x!=0)
{
if(k[x]==val){cnt[x]++;break;}
flag=(k[x]<val);
y=x;
x=ch[x][flag];
}
if(x==0){
x=++tot;
k[x]=val;
sz[x]=cnt[x]=1;
fa[x]=y;
if(y)
ch[y][flag]=x;
}
splay(x,0);
}
int getrank(int val){
int res=1,x=root;
while(x!=0)
{
if(k[x]<val)
res+=sz[ch[x][0]]+cnt[x],x=ch[x][1];
else if(k[x]>val)
x=ch[x][0];
else{
res+=sz[ch[x][0]];
break;
}
}
if(x==0)
return 0;
else
return res;
}
int select(int val){
int y=root,x;
if(val>sz[root])
return 0;
while(1)
{
x=ch[y][0];
if(sz[x]+cnt[y]<val)
{
val-=sz[x]+cnt[y];
y=ch[y][1];
}
else if(sz[x]>=val)
y=x;
else
return k[y];
}
}
int nxt(int val,bool flag){
int x=root,y=0;
while(x!=0)
{
y=x;
if(k[x]==val)break;
x=ch[x][k[x]<val];
}
if((k[y]>val&&flag==1)||(k[y]<val&&flag==0))
return y;
splay(y,0);
int tmp=ch[y][flag];
while(ch[tmp][!flag])
tmp=ch[tmp][!flag];
return tmp;
}
void del(int val) {//通过splay其前驱和后继,将其旋转到根的右儿子的左儿子处,然后删除。
//这种删除方法要在节点中加入两个虚拟节点,一个表示极小值,一个极大值
//然后求rank时要排除掉虚拟节点。
int x=nxt(val,0),y=nxt(val,1);
splay(x,0);
splay(y,x);
int z=ch[y][0];
if(z)
{
if(cnt[z]>1)
{
cnt[z]--;
splay(z,0);
}
else
{
ch[y][0]=0;
splay(y,0);
}
}
}
int main(){
int t;
getint(n);
insert(-inf);
insert(inf);
for(int i=1;i<=n;i++)
{
getint(opt),getint(t);
switch(opt)
{
case 1:{insert(t);break;}
case 2:{del(t);break;}
case 3: printf("%d\n",getrank(t)-1);break;
case 4: printf("%d\n",select(t+1));break;
case 5: printf("%d\n",k[nxt(t,0)]);break;
case 6: printf("%d\n",k[nxt(t,1)]);break;
}
}
return 0;
}