【洛谷 P1486】 [NOI2004]郁闷的出纳员 --- Splay

传送门

题目简述

维护一个数列,同时约定一个下界,当一个元素低于该下界时,将其删除。统计最终删去的数的个数
有以下操作:
1. 增加一个元素(若该元素低于下界,则无视该操作
2. 将每一个元素加上一个数
3. 将每一个元素减去一个数
4. 查询第k大的数

分析

  直接建树感觉是不可能的(整体操作绝对会Tle),因此,考虑从相对关系上对原数列进行处理。
  既然是以下界(k)来判断,那么对于整体的加减操作,则可以转化为对k的减加操作。
  对于删除节点,只需要在减工资后,将小于k的元素全部删除即可

代码

#include <cstdio>
#include <cstdlib>

#define IL inline

using namespace std;

IL int read()
{
    int k = 1, sum = 0;
    char c = getchar();
    for(; '0' > c || c > '9'; c = getchar())
    if(c == '-') k = -1;
    for(; '0' <= c && c <= '9'; c = getchar())
        sum = sum * 10 + c - '0';
    return sum * k;
}

int tot;

struct node
{
    node *father;
    node *son[2];
    int size;
    int val;
    int cnt;

    IL node(int v = 0, node *f = 0)
    {
        father = f;
        son[0] = son[1] = 0;
        size = 1;
        val = v;
        cnt = 0;
    }
};
node *root;

IL int size(node *p)
{
    return p ? p->size : 0;
}

IL bool son(node *f, node *p)
{
    return f ? (f->son[1] == p) : -1;
}

IL void connect(node *f, node *p, bool k)
{
    if(!f) root = p; else  f->son[k] = p;
    if(p) p->father = f;
}

IL void updata(node *p)
{
    p->size = size(p->son[0]) + size(p->son[1]) + p->cnt;
}

IL void rotate(node *p)
{
    node *f = p->father, *g = f->father;
    bool x = son(f, p), y = !x;

    connect(f, p->son[y], x);
    connect(g, p, son(g, f));
    connect(p, f, y);

    updata(f);
}

IL void splay(node *p, node *q)
{
    for(node *f, *g; p->father != q;)
    {
        f = p->father;
        g = f->father;

        if(g == q) rotate(p); else
        {
            if(son(g, f) ^ son(f, p))
                rotate(p), rotate(p);
            else
                rotate(f), rotate(p);
        }
    }
    updata(p);
}

IL void insert(int val)
{   
    if(!root) root = new node(val, 0); 

    for(node *p = root; p; p = p->son[val > p->val])
    {
        if(p->val == val)
        {
            ++(p->cnt);
            splay(p, 0);
            return ;
        }else
        if(!p->son[val > p->val])
            p->son[val > p->val] = new node(val, p);
    }
}

inline node *find_nxt(int val)
{
    node *x = 0;
    for(node *p = root; p; p = p->son[val > p->val])
    if(p->val >= val)
        x = p;
    return x;
}

IL void earse_2(int val)
{
    node *p = find_nxt(val);
    if(!p)
    {
        tot += size(root);
        root = 0;
    }else
    {
        splay(p, 0);
        tot += size(p->son[0]);
        p->son[0] = 0;
        updata(p);
    }
}

IL int rankx(int t)
{
    for(node *p = root; p;)
    {
        if(t <= size(p->son[0])) p = p->son[0]; else
        if(t - size(p->son[0]) <= p->cnt)
        {
            splay(p, 0);
            return p->val;
        }else
        {
            t -= size(p->son[0]) + p->cnt;
            p = p->son[1];
        }
    }
}

int main()
{
    int n = read(), del = read();

    char c;
    for(int x, sum = 0; n; -- n)
    {
        scanf(" %c", &c);
        x = read();
        if(c == 'I')
        {
            x -= del; // 此处纯属好玩,本可以无视
            if(x >= 0) insert(x + sum); // 注意先判断
        }else
        if(c == 'A')
        {
            sum -= x;
        }else
        if(c == 'S')
        {
            sum += x;
            earse_2(sum);
        }else
        if(c == 'F')
        {
            if(size(root) < x) printf("-1\n"); else
            printf("%d\n", rankx(size(root) - x + 1) - sum + del);
        }
    }
    printf("%d\n", tot);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值