题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1503
题意:
写一种数据结构,支持以下几种操作:
I 命令 新建一个工资档案,初始工资为 k。
如果某员工的初始工资低于工资下界,他将立刻离开公司。
A 命令 把每位员工的工资加上 k
S 命令 把每位员工的工资扣除 k
F 命令 查询第 k 多的工资
分析:
可以参考IOI2009 国家集训队作业 平衡树 罗穗骞
把每位员工的工资加上k和把每位员工的工资扣除k这两种操作用一个变量记录下来,没有必要把所有人的工资都访问一遍。在新建一个工资档案时,需要稍微处理一下。所以本题本质上只要支持三种操作:插入一个元素,删除一个元素,询问第 k 小的元素,判断最小的元素是否小于下界。而判断最小的元素是否小于下界可以看成是询问第 1 小元素。
用平衡树保存所有员工的工资,对于插入一个元素,删除一个元素,询问第k 小的元素等操作,都可以很好的解决。跟例1 一样,可以使用点型动态平衡树 ,每个节点保存当前区间有多少个元素。每次询问第 k 小的元素,就可以根据左右子树的节点数目决定第 k 小元素是在左子树还是右子树。每一次操作的时间复杂度都是 O(logn)。
代码:
#include<cstdio>
#include<cstdlib>
const int inf = ~0u>>2;
#define L ch[x][0]
#define R ch[x][1]
#define KT (ch[ ch[rt][1] ][0])
const int maxn = 1e5+9;
struct SplayTree {
int sz[maxn];
int ch[maxn][2];
int pre[maxn];
int rt,top;
inline void up(int x) {
sz[x] = cnt[x] + sz[ L ] + sz[ R ];
}
inline void Rotate(int x,int f) {
int y=pre[x];
ch[y][!f] = ch[x][f];
pre[ ch[x][f] ] = y;
pre[x] = pre[y];
if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1] == y ] =x;
ch[x][f] = y;
pre[y] = x;
up(y);
}
inline void Splay(int x,int goal) { //将x旋转到goal的下面
while(pre[x] != goal) {
if(pre[pre[x]] == goal) Rotate(x , ch[pre[x]][0] == x);
else {
int y=pre[x],z=pre[y];
int f = (ch[z][0]==y);
if(ch[y][f] == x) Rotate(x,!f),Rotate(x,f);
else Rotate(y,f),Rotate(x,f);
}
}
up(x);
if(goal==0) rt=x;
}
inline void RTO(int k,int goal) { //将第k位数旋转到goal的下面
int x=rt;
while(sz[ L ] != k-1) {
if(k < sz[ L ]+1) x=L;
else {
k-=(sz[ L ]+1);
x = R;
}
}
Splay(x,goal);
}
inline void Newnode(int &x,int c,int f) {
x=++top;
L = R = 0;
pre[x] = f;
sz[x]=1;
cnt[x]=1;
val[x] = c;
}
inline void init() {
ch[0][0]=ch[0][1]=pre[0]=sz[0]=0;
rt=top=0;
cnt[0]=0;
}
inline void Insert(int &x,int key,int f) {
if(!x) {
Newnode(x,key,f);
Splay(x,0);//注意插入完成后splay
return ;
}
if(key==val[x]) {
cnt[x]++;
sz[x]++;
Splay(x,0);//注意插入完成后splay
return ;
} else if(key<val[x]) {
Insert(L,key,x);
} else {
Insert(R,key,x);
}
up(x);
}
void Del_root() { //删除根节点
int t=rt;
if(ch[rt][1]) {
rt=ch[rt][1];
RTO(1,0);
ch[rt][0]=ch[t][0];
if(ch[rt][0]) pre[ch[rt][0]]=rt;
} else rt=ch[rt][0];
pre[rt]=0;
up(rt);
}
void findpre(int x,int key,int &ans) { //找前驱节点
ans=-inf;
while(x) {
if(val[x]<key) {
if(val[x]>ans)ans=val[x];
x=R;
} else x=L;
}
}
void findsucc(int x,int key,int &ans) { //找后继节点
ans=inf;
while(x) {
if(val[x]>key) {
if(val[x]<ans)ans=val[x];
// ans=min(ans,val[x]);
x=L;
} else x=R;
}
}
inline int find_kth(int x,int k) { //第k小的数
if(k<sz[L]+1) {
return find_kth(L,k);
} else if(k > sz[ L ] + cnt[x] )
return find_kth(R,k-sz[L]-cnt[x]);
else {
Splay(x,0);
return val[x];
}
}
void del(int &x,int f) { //删除小于lim的所有的数所在的节点
if(!x) return ;
if(val[x]>=lim) {
del(L,x);
} else {
sum+=sz[L]+cnt[x];
x=R;
pre[x]=f;
if(f==0) rt=x;
del(x,f);
}
if(x) up(x);
}
void solve(int n,int lim0) {
char op[5];
int x;
lim=lim0;
sum=0;
init();
while(n--){
scanf("%s%d",op,&x);
if(op[0]=='I'){
if(x<lim0)continue;
Insert(rt,x+lim-lim0,0);
}
else if(op[0]=='A'){
lim-=x;
}
else if(op[0]=='S'){
lim+=x;
if(sz[rt])del(rt,0);
}
else{
if(x>sz[rt])puts("-1");
else printf("%d\n",find_kth(rt,sz[rt]-x+1)-lim+lim0);
}
}
printf("%d\n",sum);
}
int cnt[maxn];
int val[maxn];
int lim,sum;
} spt;
int main() {
int n,lim;
while(~scanf("%d%d",&n,&lim)) {
spt.solve(n,lim);
}
return 0;
}