BZOJ 1503 [NOI2004] :郁闷的出纳员(Splay)

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

  • Input
    第一行有两个非负整数 n 和 min 。n 表示下面有多少条命令,min 表示工资下界。
    接下来的 n 行,每行表示一条命令。命令可以是以下四种之一:
    这里写图片描述
    “下划线( _ )”表示一个空格,I 命令、A 命令、S 命令中的 k 是一个非负整数,F 命令中的 k 是一个正整数。
    在初始时,可以认为公司里一个员工也没有。

  • 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

  • 题解 : Splay

对于每个Splay上的点,维护size和val.

对于几种操作的方法:

插入

先判断该点能否被插入。如果能则计数器加一。
插入时可以将m(增加减少量)先更新Splay,每个点-m(tag标记)。或者直接在加入值上+m;
最后输出离开的员工就用总员工减剩余员工。

增加、减少

外部设置计数器m,在m上加减即可。及时删除。

删除操作:

对于一个节点,可能的情况有两种:

1.当前值小于最小值。
该节点及其左子树都会被删除。将该节点右子树置为该节点父亲的儿子,更新父亲。(若该节点为根节点则将右子树置为根节点)。递归删除右子树。

2.当前值大等于最小值。
直接递归删除左子树即可。

  • Code
#include<bits/stdc++.h>
using namespace std;

const int Maxn=1e5+50;

inline int read()
{
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}

struct node
{
    node *fa,*lc,*rc;
    int val,sze,tag;
    node():fa(NULL),lc(NULL),rc(NULL){}
    void upt()
    {
        sze=(lc?lc->sze:0)+(rc?rc->sze:0)+1;
    }
}POOL[Maxn],*pool=POOL,*rt;

int n,m,M,cnt;
char ch[2];

inline node* newnode(int val)
{
    ++pool;
    pool->sze=1;
    pool->val=val;
    return pool;
}

inline bool which(node *now)
{
    return now->fa->lc==now;
}

inline void Rotate(node *x)
{
    node* y=x->fa,*z=y->fa;
    x->fa=z,y->fa=x;
    if(y->lc==x)
    {
        if(z)(z->lc==y?z->lc:z->rc)=x;
        if(x->rc)x->rc->fa=y,y->lc=x->rc;
        else y->lc=NULL;
        x->rc=y;
        y->upt();x->upt();
    }
    else
    {
        if(z)(z->lc==y?z->lc:z->rc)=x;
        if(x->lc)x->lc->fa=y,y->rc=x->lc;
        else y->rc=NULL;
        x->lc=y;
        y->upt();x->upt();
    }
}

inline void Splay(node* x)
{
    while(x->fa)
    {
        node *y=x->fa;
        if(y->fa)
        {
            if(which(x)^which(y))Rotate(x);
            else Rotate(y);
        }
        Rotate(x);
    }
    rt=x;
}

inline void pushdown(node *now,int val)
{
    now->val-=val;
    now->tag=0;
    if(now->lc)now->lc->tag+=val;
    if(now->rc)now->rc->tag+=val;
}

inline node* Insert(node* f,node*& now,int val)
{
    if(!now)
    {
        now=newnode(val);
        now->fa=f;
        return now;
    }
    if(now->tag)pushdown(now,now->tag);
    if(val<=now->val)
    {
        node* tmp=Insert(now,now->lc,val);
        now->upt();
        return tmp;
    }
    else 
    {
        node* tmp=Insert(now,now->rc,val);
        now->upt();
        return tmp;
    }
}

inline void Delete(node *now,int val)
{
    if(now->tag)pushdown(now,now->tag);
    if(now->val<val)
    {
        if(now==rt)rt=now->rc;
        if(now->rc)
        {
            now->rc->fa=now->fa;
            if(now->fa)(now->fa->lc==now?now->fa->lc:now->fa->rc)=now->rc;
            Delete(now->rc,val);
            now->rc->upt();
            if(now->fa)now->fa->upt();
        }
        else if(now->fa)
        {
            (now->fa->lc==now?now->fa->lc:now->fa->rc)=NULL;
            now->fa->upt();
        }
    }
    else
    {
        if(now->lc)
        {
            Delete(now->lc,val);
        }
        now->upt();
    }
}

inline int Find(node* now,int k)
{
    if(now->tag)pushdown(now,now->tag);
    if((now->lc?now->lc->sze:0)==(k-1))return now->val;
    else if((now->lc?now->lc->sze:0)>=k)return Find(now->lc,k);
    else return Find(now->rc,(k-(now->lc?now->lc->sze:0)-1));
}

int main()
{
    rt=NULL;
    n=read(),M=read();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch+1);
        switch(ch[1])
        {
            case 'I':
                {
                    if(rt)rt->tag+=m;
                    m=0;
                    int val=read();
                    if(val>=M)
                    {
                        cnt++;
                        if(!rt)rt=newnode(val);
                        else Splay(Insert(rt->fa,rt,val));
                    }
                    break;
                }
            case 'S':
                {
                    int x=read();
                    m+=x;
                    Delete(rt,m+M);
                    break;
                }
            case 'A':
                {
                    int x=read();
                    m-=x;
                    break;
                }
            case 'F':
                {
                    int k=read();
                    if(!rt)
                    {
                        puts("-1");
                        break;
                    }
                    k=rt->sze-k+1;
                    if(k>rt->sze||k<=0)puts("-1");
                    else cout<<Find(rt,k)-m<<endl;
                    break;
                }
        }
    }
    cout<<cnt-(rt?rt->sze:0)<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值