NOI2004 郁闷的出纳员

1503: [NOI2004]郁闷的出纳员

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 3565   Solved: 1280
[ Submit][ Status][ Discuss]

Description

OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?

Input

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

Sample Output

10
20
-1
2

HINT

I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000

Source

评测链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1503

解法:SBT。

这是一道sbt的裸题,当然,还可以用AVL、treap、线段树、树状数组解决。这里给出的是sbt的解法。

要注意的是,若给一个新人的工资低于工资下限,那么这个新人就不会成为公司员工,当然也不算做从公司离开的人。

对于sbt,我也是初学者,如果不会的,可以点击这个链接:http://pan.baidu.com/share/link?shareid=411971&uk=255096381&third=15,这里面是我初学sbt时所用的资料,并包含了本题的数据与多种标准解法(线段树、AVL等)。

代码:

#include<cstdio>
#include<algorithm>
#define maxn (100000+10)
using namespace std;

int l[maxn],r[maxn],s[maxn],p[maxn];
int root,sum,num,n,m,ans;

void init()
{
  freopen("cashier.in","r",stdin);
  freopen("cashier.out","w",stdout);
}

inline void right_rotate(int &t)
{
  int k=l[t];
  l[t]=r[k];
  r[k]=t;
  s[k]=s[t];
  s[t]=s[l[t]]+s[r[t]]+1;
  t=k;
}

inline void left_rotate(int &t)
{
  int k=r[t];
  r[t]=l[k];
  l[k]=t;
  s[k]=s[t];
  s[t]=s[l[t]]+s[r[t]]+1;
  t=k;
}

void maintain(int &t,bool flag)
{
  if(flag)
    if(s[l[l[t]]]>s[r[t]])
      right_rotate(t);
    else
      if(s[r[l[t]]]>s[r[t]])
        left_rotate(l[t]),right_rotate(t);
      else
        return;    
  else
    if(s[r[r[t]]]>s[l[t]])
      left_rotate(t);
    else
      if(s[l[r[t]]]>s[l[t]])
        right_rotate(r[t]),left_rotate(t);
      else
        return;    
  maintain(l[t],1);
  maintain(r[t],0);
  maintain(t,1);
  maintain(t,0);      
}

void insert(int &k,int w)
{ 
  if(k==0)
    {
      k=(++num);
      s[k]=1,l[k]=r[k]=0,p[k]=w;
      return;
    }
  s[k]++;
  if(w<p[k])insert(l[k],w);
  else insert(r[k],w);  
  maintain(k,w<p[k]);
}

void del(int &t)
{
  if(t==0)return;
  if(p[t]<m-sum)ans=ans+s[l[t]]+1,t=r[t],del(t);
  else del(l[t]),s[t]=s[l[t]]+s[r[t]]+1;
}

void select(int t,int k)
{
  if(s[r[t]]+1==k){printf("%d\n",p[t]+sum);return;}
  if(k>s[r[t]]){select(l[t],k-s[r[t]]-1);return;}
  select(r[t],k);
}

void work()
{
  scanf("%d%d\n",&n,&m);
  root=num=sum=ans=0;
  
  int i,k;char op;
  scanf("%c%d\n",&op,&k);
  for(i=1;i<=n;scanf("%c%d\n",&op,&k),++i)
    switch (op)
      {
        case 'I':if(k<m)break;
                 insert(root,k-sum);
                 break;        
        case 'A':sum+=k;
                 break;        
        case 'S':sum-=k,del(root);
                 break;        
        default :if(k>s[root]){printf("-1\n");break;}
                 select(root,k);
                 break;
      }
  printf("%d\n",ans);      
}

int main()
{
  init();
  work();
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值