Luogu P1486 [NOI2004]郁闷的出纳员(平衡树)

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\)

输出文件的最后一行包含一个整数,为离开公司的员工的总数。

输入输出样例

输入样例#1:

9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2

输出样例#1:

10
20
-1
2

说明

\(I\)命令的条数不超过\(100000\)

\(A\)命令和\(S\)命令的总条数不超过\(100\)

\(F\)命令的条数不超过\(100000\)

每次工资调整的调整量不超过\(1000\)

新员工的工资不超过\(100000\)

思路

在每次\(A\)\(S\)的时候,我们不去把每个人的工资更新,而是记录当前所有人的工资已经增加/减少了多少。假设当前已经变化了\(delta\)数量的工资,\(delta\)为正即为增加工资,\(delta\)为负即为减少工资,那么当我们新增一个工资为\(k\)的新员工时,就新增一个\(k-delta\)的档案,来消除他没有经历的工资变化。删除员工时,只删除当前工资小于\(min-delta\)的,查询第\(k\)大时,输出员工工资和\(delta\)的和,这样我们不用更新每个人的工资,照样可以完成所有操作。在这里,我用的是\(fhq\ Treap\)来完成各个操作。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n,salary,delta,cnt,rt,ans;
struct Node
{
    int sz,rnd,val;
    int ls,rs;
    #define sz(a) tree[a].sz
    #define rnd(a) tree[a].rnd
    #define val(a) tree[a].val
    #define ls(a) tree[a].ls
    #define rs(a) tree[a].rs
}tree[MAXN];
void update(int p){sz(p)=sz(ls(p))+sz(rs(p))+1;}
int new_node(int k)
{
    sz(++cnt)=1;
    rnd(cnt)=rand();
    val(cnt)=k;
    ls(cnt)=rs(cnt)=0;
    return cnt;
}
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(rnd(x)<rnd(y))
    {
        rs(x)=merge(rs(x),y);
        update(x);
        return x;
    }
    else
    {
        ls(y)=merge(x,ls(y));
        update(y);
        return y;
    }
}
void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else
    {
        if(val(now)>=k)
        {
            x=now;
            split(rs(now),k,rs(x),y);
        }
        else
        {
            y=now;
            split(ls(now),k,x,ls(y));
        }
        update(now);
    }
}
int kth(int now,int k)
{
    if(k==sz(ls(now))+1) return val(now);
    if(sz(ls(now))>=k) return kth(ls(now),k);
    else return kth(rs(now),k-sz(ls(now))-1);
}
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
char readc()
{
    char ch=getchar();
    while(!isalpha(ch)) ch=getchar();
    return ch;
}
int main()
{
    srand(time(0));
    n=read(),salary=read();
    while(n--)
    {
        char opt=readc();int k=read();
        if(opt=='I')
        {
            if(k<salary) continue;
            k-=delta;
            int x,y;
            split(rt,k,x,y);
            rt=merge(merge(x,new_node(k)),y);
        }
        else if(opt=='A') delta+=k;
        else if(opt=='S')
        {
            delta-=k;
            int x,y;
            split(rt,salary-delta,x,y);
            rt=x,ans+=sz(y);
        }
        else if(opt=='F')
        {
            if(k>sz(rt)) printf("%d\n",-1);
            else printf("%d\n",kth(rt,k)+delta);
        }
    }
    printf("%d",ans);
    return 0;
}

转载于:https://www.cnblogs.com/coder-Uranus/p/9887161.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值