郁闷的出纳员
【问题描述】
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。
工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。
老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。
好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
【输入文件】
第一行有两个非负整数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是一个正整数。
在初始时,可以认为公司里一个员工也没有。
【输出文件】
输出文件的行数为F命令的条数加一。
对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
【样例输入】
9 10 I 60 I 70 S 50 F 2 I 30 S 15 A 5 F 1 F 2
【样例输出】
10 20 -1 2
【约定】
l I命令的条数不超过100000
l A命令和S命令的总条数不超过100
l F命令的条数不超过100000
l 每次工资调整的调整量不超过1000
l 新员工的工资不超过100000
【题意】
要求设计一种数据结构,能够快速进行以上4种操作,完成对整个工资单的动态维护。
【分析】
这种动态问题,很明显的要用到动态的数据结构来维护,可以使用一般的线段树或者平衡树进行解决,而本题的特点非常适合Splay的发挥。
首先是看到A和S命令,都是针对整个工资单中的所有员工进行操作的,因此可以考虑不改变每个员工单独的值(n个员工就要改n次,开玩笑......),而是用另外一个独立的变量把所有的加减操作都记录下来,判断员工出局的时候再结合题目给定的最低值计算出下限。这里要注意的是,但是当一个员工新加入时,之前的调工资操作应该对他是不产生影响的,因为那时候这个人还不在,但是用来记录工资加减的独立变量只有一个,所以在新员工加入的时候要把之前的工资加减情况减掉,这样最后计算时才可以把前面的部分抵消掉。
另,若一个人的初始工资小于底线,则这个人的离开不算到最后的答案中。
插入和找第k值都是基本的二叉树很容易解决,删除操作是本题的重点:
测试模板和修改删除部分花了大把的时间..T_T
根据上面的思路,用mi表示给定的底线,tot记录工资加减情况,则最后mi-tot就是初始工资的相对底线,每次出现S,也就是减了工资之后,就需要把树中低于mi-tot的所有值都删掉。但是splay在实际使用过程中,若树中存在多个mi-tot的值,则由于中间有各种旋转、splay操作,直接查找mi-tot得到的位置不能够确定剩下的是在左子树还是右子树还是两个都有。于是采取的方案:
1.搜索mi-tot-1
2.若不存在,则插入一个mi-tot-1,将其旋转到根,然后把根和左子树都删掉!!!树中剩下的就是大于mi-tot-1,也就是大于等于mi-tot的值了,注意计数时不要忘记这个根是自己加进去的,不要算进去。
3.若存在mi-tot-1,则同样将根和左子树都删掉,然后在右子树中搜索mi-tot-1,旋转到根删掉,不断重复直到整棵树中不存在mi-tot-1
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : cashier 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 using namespace std; 13 14 15 #define MAXN 100010 16 17 int sons[MAXN][2]; 18 int father[MAXN],size[MAXN],data[MAXN]; 19 int spt=0,spttail=0,tot=0,men=0; 20 21 void rotate(int x,int w) //rotate(node,0/1) 22 { 23 int y=father[x]; 24 sons[y][1-w]=sons[x][w]; 25 if (sons[x][w]) father[sons[x][w]]=y; 26 27 father[x]=father[y]; 28 if (father[y]) 29 if (y==sons[father[y]][0]) sons[father[y]][0]=x; 30 else sons[father[y]][1]=x; 31 32 sons[x][w]=y; 33 father[y]=x; 34 35 size[x]=size[y]; 36 size[y]=size[sons[y][0]]+size[sons[y][1]]+1; 37 } 38 39 void splay(int x,int y) //splay(node,position) 40 { 41 if (!x) return ; 42 while(father[x]!=y) 43 { 44 if (father[father[x]]==y) 45 if (x==sons[father[x]][0]) rotate(x,1); 46 else rotate(x,0); 47 else 48 if (father[x]==sons[father[father[x]]][0]) 49 if (x==sons[father[x]][0]) 50 { 51 rotate(father[x],1); 52 rotate(x,1); 53 } else 54 { 55 rotate(x,0); 56 rotate(x,1); 57 } 58 else 59 if (x==sons[father[x]][1]) 60 { 61 rotate(father[x],0); 62 rotate(x,0); 63 } else 64 { 65 rotate(x,1); 66 rotate(x,0); 67 } 68 } 69 if (!y) spt=x; 70 } 71 72 void search(int x,int w) 73 { 74 while(data[x]!=w) 75 { 76 if (w<data[x]) 77 { 78 if (sons[x][0]) x=sons[x][0]; 79 else break; 80 } else if (w>data[x]) 81 { 82 if (sons[x][1]) x=sons[x][1]; 83 else break; 84 } 85 } 86 splay(x,0); 87 } 88 89 void insert(int w) //insert(value) 90 { 91 spttail++; 92 data[spttail]=w; 93 size[spttail]=1; 94 sons[spttail][0]=0; 95 sons[spttail][1]=0; 96 if (!spt) 97 { 98 father[spttail]=0; 99 spt=spttail; 100 } else 101 { 102 int x=spt; 103 while(1) 104 { 105 size[x]++; 106 if (w<data[x]) 107 if (sons[x][0]) x=sons[x][0]; 108 else break; 109 else 110 if (sons[x][1]) x=sons[x][1]; 111 else break; 112 } 113 father[spttail]=x; 114 if (w<data[x]) sons[x][0]=spttail; 115 else sons[x][1]=spttail; 116 splay(spttail,0); 117 } 118 } 119 120 void select(int x,int v) //select(root,k) 121 { 122 while(v!=size[sons[x][0]]+1) 123 { 124 if (v<=size[sons[x][0]]) x=sons[x][0]; 125 else 126 { 127 v-=size[sons[x][0]]+1; 128 x=sons[x][1]; 129 } 130 } 131 splay(x,0); 132 } 133 134 int main() 135 { 136 freopen("cashier.in","r",stdin); 137 freopen("cashier.out","w",stdout); 138 139 int n,mi; 140 scanf("%d%d",&n,&mi); 141 142 spt=0; 143 spttail=0; 144 tot=0; 145 men=0; 146 147 for (int i=1;i<=n;i++) 148 { 149 char c; 150 c=getchar(); 151 while(c!=‘I‘&&c!=‘A‘&&c!=‘S‘&&c!=‘F‘) c=getchar(); 152 int k; 153 scanf("%d",&k); 154 155 if (c==‘I‘) 156 { 157 if (k>=mi) insert(k-tot); 158 } else 159 if (c==‘A‘) 160 { 161 tot+=k; 162 } else 163 if (c==‘S‘) 164 { 165 tot-=k; 166 167 search(spt,mi-tot-1); 168 if (data[spt]!=mi-tot-1) 169 { 170 insert(mi-tot-1); 171 men+=size[sons[spt][0]]; 172 spt=sons[spt][1]; 173 father[spt]=0; 174 } else 175 { 176 men+=size[sons[spt][0]]+1; 177 spt=sons[spt][1]; 178 father[spt]=0; 179 search(spt,mi-tot-1); 180 while(data[spt]==mi-tot-1) 181 { 182 men++; 183 spt=sons[spt][1]; 184 father[spt]=0; 185 search(spt,mi-tot-1); 186 } 187 } 188 } else 189 { 190 if (k>size[spt]) printf("-1\n"); 191 else 192 { 193 select(spt,size[spt]-k+1); 194 printf("%d\n",data[spt]+tot); 195 } 196 } 197 198 //printf("Size:%d mi-tot+1:%d\n",size[spt],mi-tot); //debug 199 } 200 201 //printf("%d\n",men-size[spt]); 202 printf("%d\n",men); 203 204 return 0; 205 }