PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值)
Posted Blackops
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值)相关的知识,希望对你有一定的参考价值。
L3-002. 堆栈
大家都知道“堆栈”是一种“先进后出”的线性结构,基本操作有“入栈”(将新元素插入栈顶)和“出栈”(将栈顶元素的值返回并从堆栈中将其删除)。现请你实现一种特殊的堆栈,它多了一种操作叫“查中值”,即返回堆栈中所有元素的中值。对于N个元素,若N是偶数,则中值定义为第N/2个最小元;若N是奇数,则中值定义为第(N+1)/2个最小元。
输入格式:
输入第一行给出正整数N(<= 105)。随后N行,每行给出一个操作指令,为下列3种指令之一:
Push keyPop
PeekMedian
其中Push表示入栈,key是不超过105的正整数;Pop表示出栈;PeekMedian表示查中值。
输出格式:
对每个入栈指令,将key入栈,并不输出任何信息。对每个出栈或查中值的指令,在一行中打印相应的返回结果。若指令非法,就打印“Invalid”。
输入样例:17 Pop PeekMedian Push 3 PeekMedian Push 2 PeekMedian Push 1 PeekMedian Pop Pop Push 5 Push 4 PeekMedian Pop Pop Pop Pop输出样例:
Invalid Invalid 3 2 2 1 2 4 4 5 3 Invalid
题目链接:PAT L3-002
以前是看别人代码过的,并不懂其中的意思,由于最近想学主席树,又把这题拿出来做了一下,线段树的话应该是只能查询当前所含范围1~n内的第K大值而不是像主席树那样直接更新完可以随意查询某一个区间内的第K大,就是说线段树范围已经定死就是你build的那个范围。
做法就是对以出现的值的范围建树,然后每一次更新就把这个值对应的叶子节点++或--,用cnt记录当前区间内出现了几个数,这样一来查询就好办了,若左子树cnt大于等于k,则肯定在左子树,反之则在右子树,但此时就不是K了,因为k已经大于左子树的cnt了,就是说还剩下k-lson.cnt个数,比如我要查第10大,左边6个右边10个,一共14个数,那肯定就直接从右边开始数10-6个即右边的第4个。懂了这个就是一个裸的线段树单点更新查询稍微变化一下的题目了,用cin第二组会超时用scanf比较快
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<bitset> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> using namespace std; #define INF 0x3f3f3f3f #define CLR(x,y) memset(x,y,sizeof(x)) #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) typedef pair<int,int> pii; typedef long long LL; const double PI=acos(-1.0); const int N=1e5+10; struct seg { int l,mid,r; int cnt; }; seg T[N<<2]; void pushup(int k) { T[k].cnt=T[LC(k)].cnt+T[RC(k)].cnt; } void build(int k,int l,int r) { T[k].l=l; T[k].r=r; T[k].mid=MID(l,r); T[k].cnt=0; if(l==r) return ; build(LC(k),l,T[k].mid); build(RC(k),T[k].mid+1,r); pushup(k); } void update(int k,int x,int val) { if(T[k].l==T[k].r&&T[k].l==x) T[k].cnt+=val; else { if(x<=T[k].mid) update(LC(k),x,val); else update(RC(k),x,val); pushup(k); } } int query(int rt,int k) { if(T[rt].l==T[rt].r) return T[rt].r; else { if(k<=T[LC(rt)].cnt) return query(LC(rt),k); else return query(RC(rt),k-T[LC(rt)].cnt); } } int st[N]; int main(void) { int n,top,val; char ops[15]; while (~scanf("%d",&n)) { top=0; build(1,1,N-5); while (n--) { scanf("%s",ops); if(ops[1]==‘o‘) { if(!top) puts("Invalid"); else { update(1,st[top-1],-1); printf("%d\n",st[top-1]); --top; } } else if(ops[1]==‘u‘) { scanf("%d",&val); update(1,val,1); st[top++]=val; } else if(ops[1]==‘e‘) { if(!top) puts("Invalid"); else printf("%d\n",query(1,(top+1)>>1)); } } } return 0; }
以上是关于PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值)的主要内容,如果未能解决你的问题,请参考以下文章
树状数组+二分答案查询第k大的数 (团体程序设计天梯赛 L3-002. 堆栈)
PAT 团体程序设计天梯赛-练习集 L2-006. 树的遍历