题目:
bzoj 1503
大意:3种操作:1.插入一个数。2.给每个数加或减一个数。3.求当前数列第k大。每个数不能小于m,某一时刻小于m会被自动删去,最后输出删去了多少个数。
分析:平衡数模版题。在代码会加注释。
代码:
/**************************************************************
Problem: 1503
User: beginend
Language: C++
Result: Accepted
Time:732 ms
Memory:3244 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
using namespace std;
struct leaf
{
int k,s,l,r,num;
//k表示该点权值,s表示子树大小,l表示左子树地址,r表示右子树地址,num为随机出来的平衡因子
};
int n,m,delta=0,size=0,root=0,tot=0;
leaf t[100005]={0};
void rttl(int &x)
{
int y=t[x].r;
t[x].r=t[y].l;//该点的右儿子变为右儿子的左儿子
t[y].l=x;//右儿子变为根,x变为右儿子的左儿子
t[x].s=t[t[x].l].s+t[t[x].r].s+1;
t[y].s=t[t[y].l].s+t[t[y].r].s+1;
x=y;
}
void rttr(int &x)
{
int y=t[x].l;
t[x].l=t[y].r;
t[y].r=x;
t[x].s=t[t[x].l].s+t[t[x].r].s+1;
t[y].s=t[t[y].l].s+t[t[y].r].s+1;
x=y;
}
void insert(int &d,int x)
{
if (d==0)
{
size++;
t[size].l=t[size].r=0;
t[size].k=x;
t[size].s=1;
t[size].num=rand();
d=size;
return;
}
if (x>t[d].k)//比根大
{
insert(t[d].r,x);//插入至右子树
if (t[t[d].r].num>t[d].num) rttl(d);
}else
{
insert(t[d].l,x);//插入至左子树
if (t[t[d].l].num>t[d].num) rttr(d);
}
t[d].s=t[t[d].l].s+t[t[d].r].s+1;//更新子树大小
}
void del(int &d)
{
if (d==0) return;
if (t[d].k+delta<m)//判断根是否比min小,是则删去该点及其左子树,右子树直接连父亲
{
tot+=t[t[d].l].s+1;
d=t[d].r;
del(d);
return;
}
del(t[d].l);//否则查找左子树
t[d].s=t[t[d].l].s+t[t[d].r].s+1;
}
int find(int d,int k)//查找第k大
{
if (t[t[d].l].s==k-1)
{
return t[d].k;
}
if (t[t[d].l].s<k-1)
{
return find(t[d].r,k-t[t[d].l].s-1);
}else
{
return find(t[d].l,k);
}
}
int main()
{
scanf("%d %d",&n,&m);
while (n--)
{
char w[1];
int x;
scanf("%s%d",w,&x);
if (w[0]=='I')
{
if (x>=m) insert(root,x-delta);
}else
if (w[0]=='A')
{
delta+=x;
}else
if (w[0]=='S')
{
delta-=x;
del(root);
}else
{
if (x>t[root].s||x==0)
{
printf("%d\n",-1);
}else
{
printf("%d\n",find(root,t[root].s-x+1)+delta);
}
}
}
printf("%d\n",tot);
return 0;
}