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 32 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);
}
}
}