题目链接: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;
}