P1486 [NOI2004]郁闷的出纳员
题目描述
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。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
输入输出样例
说明
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000
//让这道板子题难死我。。。调了一天了 //换了有四五种思路,最后发现我并没有想错,就是这么写 //犯了一个很sb的错误,原因是想的方法太多了,没改过来,保持了上一种写法的样子 //但是我感觉貌似并没影响啊。。。但是它就是WA了 //一个遗漏的小细节,浪费了一下午的时间 //但是 因为绞尽脑汁地A掉这道题,所以我又学会了一些骚操作 // 但是依然感觉很亏啊 好好的时间,就这么流走了 // 思路: //正常的treap操作,删除的时候暴力枚举删除(/滑稽),不用担心TLE //本来想加个并查集维护一下删除操作,但是做出这道题来之后已经完全没心情了 //如果一个人跳槽了,就给他打个标记,下次涨工资或者减工资的时候就continue掉 //别的就和板子一个样了 //我犯的几个zz错误: //1、注意题目中说一开始工资不够离开的不加入答案 (我并没犯这个错误) //2、询问的是工资第k大的人的工资是多少,不是问第k大的工资是多少!!!——>这个很重点,就是因为这个,我换了一种思路写的代码 //3、如果你写的是左儿子比右儿子大的treap,那么查找的时候找的是(在职人数-x+1),因为你的查询查询的是第k小 // 如果是右儿子比左儿子小的,直接查找x就行了。 //5、删除的时候要让root->size-=删除的那个工资的人数,不是 --root->size //4、可能会有职工的工资是一样的,所以要记录相同工资的人数。 ->第2条和第5条的原因 //忠告:慎用指针! 虽然功能强大,但是太难调试,而且一些操作并不能像数组一样那么灵活的去执行。 // 本人不会用指针 如果是指针大佬。就当我没说。 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; const int INF=599518803; int n,minn; int opt,x; int tim,sum_peo,leave_peo; int add[N],sub[N]; struct Node { Node *son[2]; //左右儿子,son[0]是左儿子,son[1]是右儿子 int key; //权值 int heap_key; //维护堆的权值 int key_cnt; //节点的重量 int size; //子树的节点个数 int tim; //进公司的时间 bool flag; //是否跳槽 }node[N]; typedef Node* Tree; Tree now_node,root,null; int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) switch(c) { case 'I':return 1; case 'A':return 2; case 'S':return 3; case 'F':return 4; case '-':f=-1; default: break; } for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } void init() { srand(INF); //随机种子 tim=1; node->flag=1; node->son[0]=node->son[1]=node; //null的左右儿子指向null now_node=root=null=node; } Tree newNode(int key) //申请新节点 { //++sum_peo; ++now_node; now_node->key=key; now_node->key_cnt=1; now_node->heap_key=rand(); now_node->size=1; now_node->flag=0; now_node->son[0]=now_node->son[1]=null; now_node->tim=tim; return now_node; } void rotate(Tree &root,bool flag) //旋转,flag==0是左旋,==1是右旋 { Tree tmp=root->son[!flag]; root->son[!flag]=tmp->son[flag]; tmp->son[flag]=root; root->size=root->son[0]->size+root->son[1]->size+root->key_cnt; tmp->size=tmp->son[0]->size+tmp->son[1]->size+tmp->key_cnt; root=tmp; } void insert(Tree &root,int key) { if(root==null) //开新的工资档案 root=newNode(key); else if(key==root->key) //相同工资人数++ { ++root->key_cnt,++root->size; } else { bool flag=key<root->key; insert(root->son[flag],key); ++root->size; if(root->heap_key<root->son[flag]->heap_key) rotate(root,!flag); } } void erase(Tree &root,int key,int num) //num个员工跳槽 { if(root==null) return; if(key!=root->key) { bool flag=key<root->key; erase(root->son[flag],key,num); root->size-=num; } else { if(root->son[0]==null) { //change=root; //printf("A: change:%d root:%d\n",change,root); root=root->son[1]; //printf("B: change:%d root:%d\n",change,root); } else if(root->son[1]==null) { //change=root; root=root->son[0]; } else { bool flag=root->son[0]->heap_key>root->son[1]->heap_key; rotate(root,flag); erase(root->son[flag],key,num); root->size-=num; } } } int query_num(Tree root,int k) //询问第k大 { if(root==null) return 0; if(k<=root->son[0]->size) return query_num(root->son[0],k); if(k>root->son[0]->size+root->key_cnt) return query_num(root->son[1],k-root->son[0]->size-root->key_cnt); return root->key; } // //Tree find(Tree x) //{ // return x->fa==x?x:x->fa=find(x->fa); //} int main() { //freopen("233.in","r",stdin); //freopen("233.out","w",stdout); init(); //别忘调用初始化函数 n=read(),minn=read(); while(n--) { opt=read(),x=read(); switch(opt) { case 1: if(x>=minn) ++sum_peo, //在职员工人数++ insert(root,x); break; case 2: for(Tree i=node+1;i<=now_node;++i) { if(i->flag==1) //跳槽了 continue; i->key+=x; //涨工资 } break; case 3: //printf("be: %d\n",sum_peo); for(Tree i=node+1;i<=now_node;++i) { if(i->flag==1) //已经跳槽了 continue; i->key-=x; //扣工资 if(i->key<minn) { //Tree change=null; //printf("C: change: %d\n",change); erase(root,i->key,i->key_cnt); sum_peo-=i->key_cnt; //减去这个工资的人数 leave_peo+=i->key_cnt; //跳槽人数 i->flag=1; //标记一下已经跳槽 //printf("D: change: %d\n",change); //change=null; //printf("E: change: %d\n",change); } } //printf("af: %d\n",sum_peo); break; default: //printf("peo: %d\n",sum_peo); if(x>sum_peo) printf("-1\n"); else printf("%d\n",query_num(root,x)); } } printf("%d",leave_peo); return 0; }