题目链接:https://loj.ac/problem/10145
解题思路
用Treap解决,用到插入,与查找第k大,不需要特定的删除(即一个一个删除,浪费时间),如果工资减到小于合同工资,我们直接将该点与其左子树删去,并更新离开公司人的数量,并将其右子树移到该点,注意此时该点已经变成其右孩子结点了,再往下减工资,还是从该点开始,最后还要更新一下该点的子树大小。其它的套模板即可。
AC代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1e5+5;
struct node
{
int v,p,lc,rc,size,c;
#define v(x) t[x].v
#define p(x) t[x].p
#define lc(x) t[x].lc
#define rc(x) t[x].rc
#define s(x) t[x].size
#define c(x) t[x].c
}t[N];
int n,mi,tot,rt,ans;
inline int get()
{
char c;
int sign=1,res;
while((c=getchar())<'0'||c>'9')
if(c=='-')
sign=-1;
res=c-'0';
while((c=getchar())>='0'&&c<='9')
res=res*10+c-'0';
return res*sign;
}
inline void update(const int &x)//更新子树大小
{
s(x)=s(lc(x))+s(rc(x))+c(x);
}
inline void Zig(int &x)//右旋
{
int y=lc(x);
lc(x)=rc(y);
rc(y)=x;
s(y)=s(x);
update(x);
x=y;
}
inline void Zag(int &x)//左旋
{
int y=rc(x);
rc(x)=lc(y);
lc(y)=x;
s(y)=s(x);
update(x);
x=y;
}
inline void insert(int &x,const int val)//插入
{
if(!x)
{
v(x=++tot)=val;
p(x)=rand();
lc(x)=rc(x)=0;
c(x)=s(x)=1;
return ;
}
s(x)++;
if(v(x)==val)
c(x)++;
else
if(v(x)>val)
{
insert(lc(x),val);
if(p(lc(x))<p(x))
Zig(x);
}
else
{
insert(rc(x),val);
if(p(rc(x))<p(x))
Zag(x);
}
}
inline void add(int x,const int val)//涨工资
{
if(!x)
return ;
v(x)+=val;
add(lc(x),val);
add(rc(x),val);
}
inline void sub(int &x,const int val)//减工资
{
if(!x)
return ;
v(x)-=val;
if(v(x)<mi)
{
ans+=s(x)-s(rc(x));
x=rc(x);
sub(x,val);
}
else
{
sub(lc(x),val);
sub(rc(x),val);
}
update(x);
}
inline int QueryKth(int k)//找排名第k的
{
int x=rt;
while(x)
{
if(s(rc(x))<k&&s(rc(x))+c(x)>=k)
return v(x);
if(s(rc(x))>=k)
x=rc(x);
else
{
k-=s(rc(x))+c(x);
x=lc(x);
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&mi);
char opt[2];
int x;
int sum=0;
while(n--)
{
scanf("%s%d",opt,&x);
if(opt[0]=='I')
{
if(x>=mi)
{
insert(rt,x);
sum++;
}
}
else
if(opt[0]=='A')
{
add(rt,x);
}
else
if(opt[0]=='S')
{
int tmp=ans;
sub(rt,x);
sum-=ans-tmp;
}
else
{
if(sum<x)
puts("-1");
else
printf("%d\n",QueryKth(x));
}
}
printf("%d\n",ans);
return 0;
}