L3-2. 堆栈(线段树单点更新)

题目链接:https://www.patest.cn/contests/gplt/L3-2

思路分析:本题可以用一句话描述,就是求堆栈的中位数。堆栈是可以用数组模拟实现,可以用一个栈顶和栈底指针来实现数组模拟堆栈,分别用top和bottom。初始时,top=0,bottom=0,出栈的时候bottom++,进栈的时候top++。可以发现,栈中的所有元素恰好对应到数组里一段下标连续的子数组中。那么原问题就可以转化成求数组的中位数了,问题想到这里就很容易解决了,中位数即第n/2大的数,故可以用区间第K大的算法解决,但是这里出栈入栈涉及到更新,并非一般的静态区间第K大(只有询问没有修改的区间第K大),而是动态区间第K大。然后套用模板解决之。

细想之后,总感觉用区间第K大解决这个问题有点大材小用了,结合题目所给数据大小的限定,由于所给的数是0~10^5之间的数,而且查的又是整个区间的第n/2大的数,那么可以考虑建一棵维护区间里的数出现的次数的线段树。例如,区间[1,10]维护的是1,2,3,4,5,6,7,8,910总共出现多少次。

那么利用线段树单点更新即可实现维护。而查找中位数时只需要在线段树中找到第n/2大的数所在的叶子节点,叶子节点所对应的区间(叶子节点的区间l=r)值即为中位数。

AC代码:

#include<iostream>
#include<stack>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn=100002;
int sum[maxn<<2];
void pushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=0;
        return ;
    }
    int m=(l+r)>>1;
    //printf("l = %d r = %d root = %d\n",l,r,rt);
    build(lson);
    build(rson);
    pushUp(rt);
}
void update(int l,int r,int rt,int p,int v)
{
    if(l==r)
    {
        if(v==0) sum[rt]--;
        else sum[rt]++;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m) update(lson,p,v);
    else update(rson,p,v);
    pushUp(rt);
}
int query(int l,int r,int rt,int num)
{
    if(l==r) return l;
    int m=(l+r)>>1;
    if(sum[rt<<1]>=num) return query(lson,num);
    return query(rson,num-sum[rt<<1]);
}
void debug()
{
    printf("debug\n");
}
int main()
{
    //debug();
    //freopen("in.txt","r",stdin);
    int t;
    build(1,maxn,1);
    //debug();
    scanf("%d",&t);
    //cout<<t<<endl;
    stack<int>st;
    while(t--)
    {
        char buf[20];
        scanf("%s",buf);
        if(buf[1]=='o')
        {
            if(st.size())
            {
                printf("%d\n",st.top());
                update(1,maxn,1,st.top(),0);
                st.pop();
            }
            else puts("Invalid");
        }
        else if(buf[1]=='u')
        {
            int x;
            scanf("%d",&x);
            st.push(x);
            update(1,maxn,1,x,1);
        }
        else
        {
            int cnt=st.size();
            if(!cnt)
            {
                puts("Invalid");
                continue;
            }
            if(cnt&1) cnt++;
            printf("%d\n",query(1,maxn,1,cnt>>1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值