[NOI2004] 郁闷的出纳员
1503: [NOI2004]郁闷的出纳员
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 13890 Solved: 5086
[Submit][Status][Discuss]
Description
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
Input
第一行有两个非负整数n和min。n表示下面有多少条命令,min表示工资下界。
接下来的n行,每行表示一条命令。命令可以是以下四种之一:
I命令 I_k 新建一个工资档案,初始工资为k。如果某员工的初始工资低于工资下界,他将立刻离开公司。
A命令 A_k 把每位员工的工资加上k
S命令 S_k 把每位员工的工资扣除k
F命令 F_k 查询第k多的工资
_(下划线)表示一个空格,I命令、A命令、S命令中的k是一个非负整数,F命令中的k是一个正整数。
在初始时,可以认为公司里一个员工也没有。
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000
Output
输出行数为F命令的条数加一。
对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,
如果k大于目前员工的数目,则输出-1。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
10
20
-1
2
20
-1
2
题意:
你需要维护$N$个人的工资,支持以下操作:
- $I$操作:插入一个人,他的初始工资为$x$。
- $A$操作:给所有人的工资加上$x$。
- $S$操作:给所有人的工资减去$x$,如果有人的工资低于了工资下届$M$则把他删除并计入删除总人数$ans$。
- $F$操作:查询现在排名第$x$多的工资是多少,如果人数不足$x$则输出$-1$。
最后输出删除总人数$ans$。
题解:
这道题和平衡树模板的区别只在于$A$操作和$S$操作,也就是说我们需要寻求一个方法能够快速修改平衡树中所有点的点权。
注意到$A,S$操作数量很少,所以暴力修改其实就可以通过本题,但这么做也太没有梦想了吧……
我们发现所有的修改都是对于当前树中所有节点而不是一部分节点,那么是否可以维护一个$dat$表示当前树中所有点共同的增量呢?
虽然树中的点不是同时插入的,但我们如果要把这个增量强行打在新点$x$上,是不是把输入的$x$变成了$x-dat$就行了?
显然是可以的,因为$x-dat$加上现在的增量$dat$还等于$x$,并且不影响它与树中其他节点的大小关系。
那么加操作直接将$dat+x$,减操作先修改$dat$再删除所有权值$<M-dat$的点就好了。
复杂度$O(N\times log(N))$。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAXN 100005 #define MAXM 500005 #define INF 0x7fffffff #define ll long long struct node{ int v,f,siz,cnt,ch[2]; }tr[MAXN]; int rt,tot,dat,ans; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline bool getf(int k){return tr[tr[k].f].ch[1]==k;} inline void update(int k){ tr[k].siz=tr[k].cnt; tr[k].siz+=tr[tr[k].ch[0]].siz; tr[k].siz+=tr[tr[k].ch[1]].siz; return; } inline void rot(int k){ int f1=tr[k].f,f2=tr[f1].f; bool opt=getf(k); tr[f1].ch[opt]=tr[k].ch[opt^1]; tr[tr[k].ch[opt^1]].f=f1; tr[k].ch[opt^1]=f1; tr[f1].f=k;tr[k].f=f2; if(f2) tr[f2].ch[tr[f2].ch[1]==f1]=k; update(f1);update(k); return; } inline void splay(int k){ for(int fa;fa=tr[k].f;rot(k)) if(tr[fa].f) rot(getf(k)==getf(fa)?fa:k); rt=k;return; } inline void ins(int x){ if(!rt){ rt=++tot;tr[tot].v=x; tr[tot].siz=tr[tot].cnt=1; return; } int now=rt,fa=0; while(1){ if(x==tr[now].v){ tr[now].cnt++; update(now);update(fa); splay(now);break; } fa=now;now=tr[now].ch[x>tr[now].v]; if(!now){ tr[++tot].v=x;tr[tot].f=fa; tr[tot].siz=tr[tot].cnt=1; tr[fa].ch[x>tr[fa].v]=tot; update(fa);splay(tot); break; } } return; } inline int qnum(int x){ int now=rt; while(1){ if(tr[tr[now].ch[0]].siz<x && tr[tr[now].ch[0]].siz+tr[now].cnt>=x) return tr[now].v; else if(tr[tr[now].ch[0]].siz>=x) now=tr[now].ch[0]; else x-=tr[tr[now].ch[0]].siz+tr[now].cnt,now=tr[now].ch[1]; } } inline void del(){ ans+=tr[rt].cnt+tr[tr[rt].ch[0]].siz-1; rt=tr[rt].ch[1];tr[rt].f=0; return; } int main(){ //freopen("cashier2.in","r",stdin); //freopen("cashier1.out","w",stdout); int N=read(),M=read(),sum=0; while(N--){ char c;cin>>c;int x=read(); if(c=='I' && x>=M) sum++,ins(x-dat); if(c=='A') dat+=x; if(c=='S') dat-=x,ins(M-dat-1),del(); if(c=='F'){ if(x>sum-ans) printf("-1\n"); else printf("%d\n",qnum(sum-ans-x+1)+dat); } } printf("%d\n",ans); return 0; }