HYSBZ - 1503 : 郁闷的出纳员 (Treap 平衡树)

题目链接https://vjudge.net/problem/HYSBZ-1503

分析

此题中描述的各种命令可以用Treap来高效实现.
对于I k命令, 直接调用Treap的插入操作即可.
对于F k命令, 也可直接调用Treap的查询第k小的操作.
对于A k和S k命令, 假如直接暴力修改整棵树的结点, 那么会超时. 其中一个优化方法就是将结点中存的绝对工资改为相对工资, 再设立一个基准工资(0工资点) base, 当我们执行A k命令时, 令base = base + k, 当我们执行S k命令时, 则令base = base - k, 这样, 实际工资 = 相对工资 + base. 当我们执行S k命令时, 可能需要删去结点, 可以使用优先队列来优化.

代码

#include<bits/stdc++.h>
#define x first
#define y second
#define ok puts("ok");
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const double PI = acos(-1.0);
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const int N=1e5+9;
const int shift=1e3+9;
const double Eps=1e-7;

char c;
int n, mi, t, cnt, base;

//Treap结点
struct node {
    node *left, *right;             //左儿子, 右儿子
    int salary, weight, size, fix;  //薪水, 具有同一薪水的人数(一个优化), 子树大小, 修正值
    inline int lsize() {return left?left->size:0;}      //求左儿子的大小
    inline int rsize() {return right?right->size:0;}    //求右儿子的大小
};

//左旋
void leftRotate(node *&a) {
    node *b = a->right;
    a->right = b->left;
    b->left = a;
    a = b;
    b = a->left;
    b->size = b->lsize() + b->rsize() + b->weight;  //更新结点的子树大小
    a->size = a->lsize() + a->rsize() + a->weight;
}

//右旋
void rightRotate(node *&a) {
    node *b = a->left;
    a->left = b->right;
    b->right = a;
    a = b;
    b = a->right;
    b->size = b->lsize() + b->rsize() + b->weight;  //更新结点的子树大小
    a->size = a->lsize() + a->rsize() + a->weight;
}

//插入
void insert(node *&p, int salary) {
    if(!p) {    //创建新结点
        p = new node;
        p->fix = rand();
        p->salary = salary;
        p->size = p->weight = 1;
        p->left = p->right = NULL;
    }
    else if(p->salary == salary) {  //假如已经存在有同一薪水的结点, 则让该结点的权重加一
        p->weight++;
    }
    else if(salary < p->salary) {
        insert(p->left, salary);
        if(p->left->fix < p->fix)
            rightRotate(p);
    }
    else {
        insert(p->right, salary);
        if(p->right->fix < p->fix)
            leftRotate(p);
    }
    p->size = p->lsize() + p->rsize() + p->weight;
}

//删除
void del(node *&p, int salary) {
    if(p->salary == salary) {
        if(p->weight > 1) {         //假如存在结点, 且该结点的权重大于1, 则仅让该点权重减一
            p->weight--;
        }
        else if(!p->left || !p->right) {        //该点是叶结点或链结点
            node *t = p;
            if(!p->right) 
                p = p->left;
            else
                p = p->right;
            delete t;
        }
        else if(p->left->fix < p->right->fix){  //该点有两个儿子
            rightRotate(p);
            del(p, salary);
        }
        else {
            leftRotate(p);
            del(p, salary);
        }
    }
    else if(salary < p->salary)     //递归查找删除
        del(p->left, salary);
    else
        del(p->right, salary);
    if(p)                           //更新子树大小
        p->size = p->lsize() + p->rsize() + p->weight;
}

//查找薪资排名第k小的
int findK(node *&p, int k) {
    if(k <= p->lsize())
        return findK(p->left, k);
    else if(k > p->lsize() + p->weight)
        return findK(p->right, k-(p->lsize()+p->weight));
    else
        return p->salary;
}

int main(void) {
    //if(fopen("in", "r")!=NULL) {freopen("in", "r", stdin); freopen("out", "w", stdout);}
    node *root = NULL;
    scanf("%d%d", &n, &mi);
    priority_queue<int, vi, greater<int> > que;
    while(n--) {
        getchar();
        c = getchar();
        scanf("%d", &t);
        if(c == 'I') {
            if(t < mi) continue;
            insert(root, t-base);
            que.push(t-base);
        }
        else if(c == 'A') {
            base += t;
        }
        else if(c == 'S') {
            base -= t;
            while(!que.empty()) {
                int t1 = que.top();
                if(t1 + base < mi) {
                    que.pop();
                    del(root, t1);
                    cnt++;
                }
                else
                    break;
            }
        }
        else {  //查询
            if(root == NULL || root->size < t) {
                printf("-1\n");
                continue;
            } 
            int ans = findK(root, root->size-t+1);
            printf("%d\n", ans + base);
        }
    }
    printf("%d\n",cnt);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值