bzoj 1503: [NOI2004]郁闷的出纳员 平衡树treap

本文提供了一道编号为 BZOJ1503 的算法题目解析,涉及平衡数模板的实现。具体包括三种操作:插入元素、调整所有元素值以及查询第 k 大的元素。代码中详细解释了平衡数树的插入、删除及查找等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
bzoj 1503

大意:3种操作:1.插入一个数。2.给每个数加或减一个数。3.求当前数列第k大。每个数不能小于m,某一时刻小于m会被自动删去,最后输出删去了多少个数。

分析:平衡数模版题。在代码会加注释。

代码:

/**************************************************************
    Problem: 1503
    User: beginend
    Language: C++
    Result: Accepted
    Time:732 ms
    Memory:3244 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
using namespace std;

struct leaf
{
    int k,s,l,r,num;
    //k表示该点权值,s表示子树大小,l表示左子树地址,r表示右子树地址,num为随机出来的平衡因子
};

int n,m,delta=0,size=0,root=0,tot=0;
leaf t[100005]={0};

void rttl(int &x)
{
    int y=t[x].r;
    t[x].r=t[y].l;//该点的右儿子变为右儿子的左儿子
    t[y].l=x;//右儿子变为根,x变为右儿子的左儿子
    t[x].s=t[t[x].l].s+t[t[x].r].s+1;
    t[y].s=t[t[y].l].s+t[t[y].r].s+1;
    x=y;
}

void rttr(int &x)
{
    int y=t[x].l;
    t[x].l=t[y].r;
    t[y].r=x;
    t[x].s=t[t[x].l].s+t[t[x].r].s+1;
    t[y].s=t[t[y].l].s+t[t[y].r].s+1;
    x=y;
}

void insert(int &d,int x)
{
    if (d==0)
    {
        size++;
        t[size].l=t[size].r=0;
        t[size].k=x;
        t[size].s=1;
        t[size].num=rand();
        d=size;
        return;
    }
    if (x>t[d].k)//比根大
    {
        insert(t[d].r,x);//插入至右子树
        if (t[t[d].r].num>t[d].num) rttl(d);
    }else
    {
        insert(t[d].l,x);//插入至左子树
        if (t[t[d].l].num>t[d].num) rttr(d);
    }
    t[d].s=t[t[d].l].s+t[t[d].r].s+1;//更新子树大小
}

void del(int &d)
{
    if (d==0) return;
    if (t[d].k+delta<m)//判断根是否比min小,是则删去该点及其左子树,右子树直接连父亲
    {
        tot+=t[t[d].l].s+1;
        d=t[d].r;
        del(d);
        return;
    }
    del(t[d].l);//否则查找左子树
    t[d].s=t[t[d].l].s+t[t[d].r].s+1;
}

int find(int d,int k)//查找第k大
{
    if (t[t[d].l].s==k-1)
    {
        return t[d].k;
    }
    if (t[t[d].l].s<k-1)
    {
        return find(t[d].r,k-t[t[d].l].s-1);
    }else
    {
        return find(t[d].l,k);
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    while (n--)
    {
        char w[1];
        int x;
        scanf("%s%d",w,&x);
        if (w[0]=='I')
        {
            if (x>=m) insert(root,x-delta);
        }else
        if (w[0]=='A')
        {
            delta+=x;
        }else
        if (w[0]=='S')
        {
            delta-=x;
            del(root);
        }else
        {
            if (x>t[root].s||x==0)
            {
                printf("%d\n",-1);
            }else
            {
                printf("%d\n",find(root,t[root].s-x+1)+delta);
            }
        }
    }
    printf("%d\n",tot);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值