特殊堆栈(线段树题解)
堆栈是一种经典的后进先出的线性结构,相关的操作主要有“入栈”(在堆栈顶插入一个元素)和“出栈”(将栈顶元素返回并从堆栈中删除)。本题要求你实现另一个附加的操作:“取中值”——即返回所有堆栈中元素键值的中值。给定 N 个元素,如果 N 是偶数,则中值定义为第 N/2 小元;若是奇数,则为第 (N+1)/2 小元。
输入格式:
输入的第一行是正整数 N()。随后 N 行,每行给出一句指令,为以下 3 种之一:
复制Push key
Pop
PeekMedian
其中 key
是不超过 的正整数;Push
表示“入栈”;Pop
表示“出栈”;PeekMedian
表示“取中值”。
输出格式:
对每个 Push
操作,将 key
插入堆栈,无需输出;对每个 Pop
或 PeekMedian
操作,在一行中输出相应的返回值。若操作非法,则对应输出 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
作者
陈越
单位
浙江大学
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
如题所示:
正解:线段树or树状数组;
刚开始想这题想不明白该怎么使用数据结构来解决,然后看了一个大佬树状数组的题解,观察到本题是一个类似于桶排序的方法来解决,由于线段树能维护具有可加性的数据,因为此题范围[1,1e5]那么建树就利用的是每个整数值在序列中出现的次数,二分查找[1,mid]区间出现次数,即可求解;
代码如下:
复制
/*************************************************************************
> File Name: pta.cpp
# Author: Badwoman
# mail: 1194446133@qq.com
> Created Time: 2020年11月26日 星期四 15时19分09秒
************************************************************************/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
using namespace std;
const int N = 1e6+10,inf = 1e5;
int sum[N];
int n,tot,now[N/10];char op[99];
void add(int s,int t,int k,int y,int p){
int mid = (s+t)>>1;
if(s==k&&t==k){
sum[p] += y;
return;
}
if(mid>=k){
add(s,mid,k,y,p<<1);
}
else add(mid+1,t,k,y,p<<1|1);
sum[p] = sum[p<<1]+sum[p<<1|1];
return;
}
int found(int l,int r,int s,int t,int p){
if(l<=s&&t<=r){
return sum[p];
}
int ans = 0,mid = (s+t)>>1;
if(l<=mid)ans += found(l,r,s,mid,p<<1);
if(r>mid)ans += found(l,r,mid+1,t,p<<1|1);
return ans ;
}int mx = -1;
int getans(int x){
int l = 1,r = mx ;
int sum = 0;
while(l<r){
int mid = (l+r)>>1;
sum = found(1,mid,1,inf,1);
if(sum>=x) r = mid;
else l = mid + 1;
}
return l;
}
void solve(){
scanf("%d",&n);
while(n--){
scanf("%s",op);
if(op[0] == 'P'&&op[1] == 'o'){
if(!tot)puts("Invalid");
else {
add(1,inf,now[tot--],-1,1);
printf("%d\n",now[tot+1]);
}
}
else if(op[1]=='u'){
scanf("%d",&now[++tot]);
mx = max(mx,now[tot]);
add(1,inf,now[tot],1,1);
}
else {
if(!tot){
puts("Invalid");continue;
}
int t = (tot+1)>>1;
printf("%d\n",getans(t));
}
}
return;
}
int main(){
solve();
return 0;
}
这个建树是我没想到的,当初的想法想到了或许建树时建1e5个节点,最初设为0;而类似桶排序的思路则是网上树状数组题解提供给我的。这个题感觉是二分答案?二分答案类的题目现在有点不敢想,因为二分答案大多数范围都很大,但其实仔细一想,就多了一个log复杂度,当正向想不出答案时,不妨利用逆向思维,在答案这个集合中进行考虑;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律