Treap(三)——#10145. 「一本通 4.6 练习 2」郁闷的出纳员

题目链接:https://loj.ac/problem/10145
解题思路
用Treap解决,用到插入,与查找第k大,不需要特定的删除(即一个一个删除,浪费时间),如果工资减到小于合同工资,我们直接将该点与其左子树删去,并更新离开公司人的数量,并将其右子树移到该点,注意此时该点已经变成其右孩子结点了,再往下减工资,还是从该点开始,最后还要更新一下该点的子树大小。其它的套模板即可。
AC代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1e5+5;
struct node
{
    int v,p,lc,rc,size,c;
    #define v(x) t[x].v
    #define p(x) t[x].p
    #define lc(x) t[x].lc
    #define rc(x) t[x].rc
    #define s(x) t[x].size
    #define c(x) t[x].c
}t[N];
int n,mi,tot,rt,ans;
inline int get()
{
    char c;
    int sign=1,res;
    while((c=getchar())<'0'||c>'9')
    if(c=='-')
    sign=-1;
    res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=res*10+c-'0';
    return res*sign;
}
inline void update(const int &x)//更新子树大小
{
    s(x)=s(lc(x))+s(rc(x))+c(x);
}
inline void Zig(int &x)//右旋
{
    int y=lc(x);
    lc(x)=rc(y);
    rc(y)=x;
    s(y)=s(x);
    update(x);
    x=y;
}
inline void Zag(int &x)//左旋
{
    int y=rc(x);
    rc(x)=lc(y);
    lc(y)=x;
    s(y)=s(x);
    update(x);
    x=y;
}
inline void insert(int &x,const int val)//插入
{
    if(!x)
    {
        v(x=++tot)=val;
        p(x)=rand();
        lc(x)=rc(x)=0;
        c(x)=s(x)=1;
        return ;
    }
    s(x)++;
    if(v(x)==val)
    c(x)++;
    else
    if(v(x)>val)
    {
        insert(lc(x),val);
        if(p(lc(x))<p(x))
        Zig(x);
    }
    else
    {
        insert(rc(x),val);
        if(p(rc(x))<p(x))
        Zag(x);
    }
}
inline void add(int x,const int val)//涨工资
{
    if(!x)
    return ;
    v(x)+=val;
    add(lc(x),val);
    add(rc(x),val);
}
inline void sub(int &x,const int val)//减工资
{
    if(!x)
    return ;
    v(x)-=val;
    if(v(x)<mi)
    {
        ans+=s(x)-s(rc(x));
        x=rc(x);
        sub(x,val);
    }
    else
    {
        sub(lc(x),val);
        sub(rc(x),val);
    }
    update(x);
}
inline int QueryKth(int k)//找排名第k的
{
    int x=rt;
    while(x)
    {
        if(s(rc(x))<k&&s(rc(x))+c(x)>=k)
        return v(x);
        if(s(rc(x))>=k)
        x=rc(x);
        else
        {
            k-=s(rc(x))+c(x);
            x=lc(x);
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&mi);
    char opt[2];
    int x;
    int sum=0;
    while(n--)
    {
        scanf("%s%d",opt,&x);
        if(opt[0]=='I')
        {
            if(x>=mi)
            {
                insert(rt,x);
                sum++;
            }
        }
        else
        if(opt[0]=='A')
        {
            add(rt,x);
        }
        else
        if(opt[0]=='S')
        {
            int tmp=ans;
            sub(rt,x);
            sum-=ans-tmp;
        }
        else
        {
            if(sum<x)
            puts("-1");
            else
            printf("%d\n",QueryKth(x));
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值