bzoj 1503: [NOI2004]郁闷的出纳员

Description

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

solution

正解:平衡树
我们维护一个全局变量\(f\),为之前扣除量之和,每一次扣除我们把下界加上k,并把小于下界的都删除,\(split\)一下前几个即可,每次插入的时候,初始工资要加上\(f\),并且离开的人数总和不算一开始就离开的

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=100005;
typedef long long ll;
int Q,lim,ch[N][2],key[N],sz[N],v[N],rt,cnt=0,lazy[N],f=0;
inline void upd(int x){if(x)sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
inline void down(int x){
    if(!x || !lazy[x])return ;
    int ls=ch[x][0],rs=ch[x][1],k=lazy[x];
    if(ls)v[ls]+=k,lazy[ls]+=k;
    if(rs)v[rs]+=k,lazy[rs]+=k;
    lazy[x]=0;
}
inline void split(int x,int k,int &a,int &b){
    if(!k){a=0;b=x;return ;}
    down(x);
    int l=ch[x][0],r=ch[x][1];
    if(sz[l]==k)ch[x][0]=0,a=l,b=x;
    else if(sz[l]+1==k)ch[x][1]=0,a=x,b=r;
    else if(sz[l]>k)split(l,k,a,ch[x][0]),b=x;
    else split(r,k-sz[l]-1,ch[x][1],b),a=x;
    upd(x);
}
inline int merge(int x,int y){
    if(!x || !y)return x+y;
    down(x);down(y);
    if(key[x]>key[y]){
        ch[x][1]=merge(ch[x][1],y);
        upd(x);
        return x;
    }
    else{
        ch[y][0]=merge(x,ch[y][0]);
        upd(y);
        return y;
    }
}
inline int rank(int k){
    int ret=1,x=rt;
    while(x){
        down(x);
        if(k<=v[x])x=ch[x][0];
        else ret+=sz[ch[x][0]]+1,x=ch[x][1];
    }
    return ret;
}
inline void ins(int x){
    if(x<lim)return ;
    int l,r;
    v[++cnt]=x;key[cnt]=rand();sz[cnt]=1;
    split(rt,rank(x)-1,l,r);
    rt=merge(merge(l,cnt),r);
}
inline void Delet(int k){int l;split(rt,k,l,rt);}
inline int kth(int k){
    int x=rt,sum;
    while(x){
        down(x);
        sum=sz[ch[x][1]];
        if(sum==k-1)return v[x];
        if(sum>=k)x=ch[x][1];
        else k-=sum+1,x=ch[x][0];
    }return -1;
}
void work()
{
    int x,y,ans=0;char S[3];
    scanf("%d%d",&Q,&lim);
    while(Q--){
        scanf("%s%d",S,&x);
        if(S[0]=='I')ins(x+f);
        else if(S[0]=='A')v[rt]+=x,lazy[rt]+=x;
        else if(S[0]=='S'){
            f+=x,lim+=x;
            y=rank(lim)-1;
            if(y>=1)Delet(y),ans+=y;
        }
        else y=kth(x),printf("%d\n",y==-1?-1:y-f);
    }
    printf("%d\n",ans);
}
     
int main()
{
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    work();
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/8097332.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值