1503: [NOI2004]郁闷的出纳员
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 12785 Solved: 4577
[Submit][Status][Discuss]
Description
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
Input
Output
输出文件的行数为F命令的条数加一。对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
20
-1
2
HINT
I命令的条数不超过100000 A命令和S命令的总条数不超过100 F命令的条数不超过100000 每次工资调整的调整量不超过1000 新员工的工资不超过100000
分析:郁闷的程序员......这种样子的题一看就是要用数据结构来实现.考虑用splay来实现.对于I操作,直接插入就可以了.A,S两种操作是区间修改.因为是对整体修改,所以可以用一个变量sum表示当前的工资下界与最开始的工资下界相差多少.这样如果要插入一个数x,插入x-sum就可以了,最后+sum就能够还原了.每次S操作后要删除低于工资下界的人,维护一个特征点min - sum,为了方便处理,将这个点插入到splay中,转到根部,那么它的左子树连同它自己都要被删掉.保留右子树.
需要注意的是可能会有多个数相同,那么删除就不能直接删掉整个点了,而要判断当前点的个数是否 > 1.第k大是从大到小第k个,而不是从小到大.还要及时更新size数组.一旦一个点的cnt或者size变了,就要更新它的父节点,连同自己.
以前用的模板在这道题上有问题,换了一个更好的模板:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010; int n,minn,root,tot,sizee[maxn],ans,sum; bool flag = false; struct node { int left,right,fa,v,cnt; }e[maxn]; void update(int x) { sizee[x] = e[x].cnt; if (e[x].left != 0) sizee[x] += sizee[e[x].left]; if (e[x].right != 0) sizee[x] += sizee[e[x].right]; } int getsize(int x) { if (x == 0) return 0; return sizee[x]; } void turnr(int x) { int y = e[x].fa; int z = e[y].fa; e[y].left = e[x].right; if (e[x].right != 0) e[e[x].right].fa = y; e[x].fa = z; if (z != 0) { if (e[z].left == y) e[z].left = x; else e[z].right = x; } e[x].right = y; e[y].fa = x; update(x); update(y); } void turnl(int x) { int y = e[x].fa; int z = e[y].fa; e[y].right = e[x].left; if (e[x].left != 0) e[e[x].left].fa = y; e[x].fa = z; if (z != 0) { if (e[z].left == y) e[z].left = x; else e[z].right = x; } e[x].left = y; e[y].fa = x; update(x); update(y); } void splay(int x) { while (e[x].fa != 0) { int y = e[x].fa; int z = e[y].fa; if (z == 0) { if (x == e[y].left) turnr(x); else turnl(x); } else { if (e[z].left == y && e[y].left == x) { turnr(y); turnr(x); } else { if (e[z].right == y && e[y].right == x) { turnl(y); turnl(x); } else { if (e[z].left == y && e[y].right == x) { turnl(x); turnr(x); } else { turnr(x); turnl(x); } } } } } root = x; } void insert(int x) { if (!root) { ++tot; e[tot].left = e[tot].right = e[tot].fa = 0; e[tot].v = x; e[tot].cnt = sizee[tot] = 1; root = tot; return; } int now = root,fa = 0; while (1) { if(e[now].v == x) { e[now].cnt++; update(now); update(fa); splay(now); break; } fa = now; if (e[now].v < x) now = e[now].right; else now = e[now].left; if (!now) { ++tot; e[tot].left = e[tot].right = 0; e[tot].fa = fa; e[tot].v = x; e[tot].cnt = sizee[tot] = 1; if (e[fa].v < x) e[fa].right = tot; else e[fa].left = tot; update(fa); splay(tot); break; } } } void clear(int x) { e[x].fa = e[x].left = e[x].right = e[x].v = e[x].cnt = sizee[x] = 0; } void del() { ans += sizee[e[root].left]; e[root].left = 0; if (e[root].cnt > 1) { e[root].cnt--; update(root); return; } if (!e[root].right) { root = 0; return; } root = e[root].right; e[root].fa = 0; } int query(int x) { int p = root; while (p != 0) { if (e[p].right != 0 && x <= sizee[e[p].right]) p = e[p].right; else { int temp = getsize(e[p].right) + e[p].cnt; if (x <= temp) return e[p].v; x -= temp; p = e[p].left; } } return e[p].v; } int main() { scanf("%d%d",&n,&minn); for (int i = 1; i <= n; i++) { char op[2]; int k; scanf("%s",op); scanf("%d",&k); if (op[0] == 'I') { if (k < minn) continue; insert(k - sum); } if (op[0] == 'A') sum += k; if (op[0] == 'S') { sum -= k; insert(minn - sum); del(); } if (op[0] == 'F') { update(root); if (sizee[root] < k) puts("-1"); else printf("%d ",query(k) + sum); } } printf("%d ",ans); return 0; }