splay入门题---中位数

mid

Description

维护一个集合,有2种操作:
1.每次可以插入一个元素。
2.找出当前集合中第[n+1/2]大的元素,把它输出,然后把它从集合中删除。
N<=100000

Input

第1行一个数N,表示由N次操作。接下来N行。每行第1个数C表示操作类型。
1.c=1,接下来还有一个数,表示要插入元素。
2.c=2,表示要进行第2种操作。

Output

一共m行。按顺序对应输入中的第2种情况。

Sample Input

5
1 3
2
1 5
1 42

Sample Output

3

5


此题为splay入门题,动态维护求中位数,单点插入和删除,动态查询。

删除我用的是找前驱来覆盖值,同时用-inf来作为树中一定存在的点,防止删除和插入出错。教训:splay在插入后一定要splay来保证树的深度不会退化到O(n)。

#include<cstdio>		
#define maxn 100500
using namespace std;
int son[maxn][2],val[maxn],op[maxn],fa[maxn],size[maxn];
int n,m,root,tot;
void update(int x){	size[x]=size[son[x][0]]+size[son[x][1]]+1;	}

void rotate(int x){
int y=fa[x],z=fa[y];
int nx=op[x],ny=op[y];
	
if (z) son[z][ny]=x;
op[x]=ny; fa[x]=z;

z=son[x][nx^1];				
fa[z]=y; op[z]=nx; son[y][nx]=z;
fa[y]=x; op[y]=nx^1; son[x][nx^1]=y;
update(y);  update(x);
}

void splay(int x){
while (op[x]!=2)
   {
	  if (op[fa[x]]==op[x])
	  	rotate(fa[x]);
      else
      	rotate(x);
	  if (op[x]!=2)
	  	rotate(x);
   }
root=x;
}

int find_rank(int x,int y){
if (y>size[x]) return -1;
if (y<=size[son[x][1]]) return find_rank(son[x][1],y);
if (y==size[son[x][1]]+1) return x;
return find_rank(son[x][0],y-size[son[x][1]]-1);
}

int find_pre(int x){
x=son[x][0];
while (son[x][1])
	x=son[x][1];
return x;
}

void splay_delete(int x){
splay(x);
int y=find_pre(x),z=fa[y],y_son=son[y][0];
val[x]=val[y];
int ny=op[y];
if (z)	son[z][ny]=y_son; 
if (y_son)
{
	 fa[y_son]=z;
	 op[y_son]=ny;
}

fa[y]=0; op[y]=0; son[y][0]=0; size[y]=0;
y=z;
while (y!=x)
{
	size[y]--;
	y=fa[y];
}
size[x]--;
}

void splay_insert(int x){
int xx=root;
int opp=val[xx]<val[x];
while (son[xx][opp])
	{
		size[xx]++;
		xx=son[xx][opp];
		opp=val[xx]<val[x];
	}
size[xx]++;
son[xx][opp]=x;
op[x]=opp;
size[x]=1;
fa[x]=xx;
splay(x);
}



int main(){
scanf("%d",&n);
root=1;
tot=1;
size[1]=1;
op[1]=2;
val[1]=-2147483647;
for (int i=1;i<=n;i++)
{
	int a,b;
	read(a);
	if (a==1)
		{
			scanf("%d",&b);
			val[++tot]=b;
			splay_insert(tot);
		}
	else
		{
			b=find_rank(root,(size[root])/2);
			printf("%d\n",val[b]);
			splay_delete(b);
		}
}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值