题目描述
在如今的网络中,TCP 是一种被广泛使用的网络协议,它在传输层提供了可靠的通信服务。
众所周知,网络是存在时延的,例如用户先后向服务器发送了两个指令
op1
和
op2
,并且希
望服务器先处理指令
op1
,再处理指令
op2
;但由于网络时延,这两个指令可能会失序到达,
而导致服务器先执行了指令
op2
,这是我们不希望看到的。TCP 协议拥有将失序到达的报文
按顺序重组的功能,一种方法是给每一个报文打上一个时间戳。而你今天要实现的功能比这
个要简单很多。我们需要你维护一个服务器,这个服务器的功能是一个简单的栈,你会接收
三种用户的指令:
push
x
peak
t
— 用户询问现在栈顶元素的值,这条指令的时间戳为
当一条时间戳为
t
的指令到达时,你需要进行如下处理:
1.将所有之前执行的时间戳大于
2.执行当前这条指令
3.按时间戳顺序重新执行在第
1
步被撤销的指令
注意你不需要撤销以及重新执行之前已经执行过的
有在它到达的时候会被执行一次。
我们保证每一条指令的时间戳都是唯一的;若你在需要执行一条
pop
指令时发现当前栈为
空,则当前你可以忽略这条指令。
输入
第一行包含一个整数
n
,表示指令总数。
接下来
push
x
pop
t
输出
对于每一条
样例输入:
7
push 100 3
push 200 7
peak 4
push 50 2
pop 5
peak 6
peak 8
样例输出
100
50
200
数据范围
对于 10%的数据,1 <=
n
<= 1000;
对于 40%的数据,1 <=
对于 100%的数据,1 <=
n
<= 300000,0 <=
思路
虽然题目非常绕口,但是可以归纳为:在查询时间
t
的栈顶元素时,从头开始离线对所有时刻小于等于
那么我们可以先对所有的
t
离散化以减少空间消耗,然后维护一个线段树,线段树上的序列下标对应的就是时间点,线段树上某个位置
容易想到在线段树上维护区间和标记
sum[]
,一个比较好做的做法就是二分
i
,每次对区间求和然后修改上下界,这样的做法是
然后考虑更快的做法:维护线段树上的区间内最大后缀和标记
maxright
,将
[1,t]
这段区间,拆分成尽量少的线段树结点,然后从最右边那个结点开始暴力扫,维护当前扫过的结点对应区间和
sum
,若扫到结点
j
,且
评测结果
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 310000
#define lson (o<<1)
#define rson (o<<1|1)
#define INF 0x3f3f3f3f
using namespace std;
//num[i]=第i次操作入栈的元素(假如第i次操作是入栈的话),val[i]=时刻i入栈的元素(假如时刻i存在入栈操作)
int cmd[MAXN],t[MAXN],num[MAXN],data[MAXN],val[MAXN]; //t[i]=离散化后第i次操作的时间
int sum_tag[MAXN<<2],maxright_tag[MAXN<<2]; //sum_tag[o]=节点o对应区间和,maxright_tag[o]=节点o中最大的后缀和
void pushup(int o)
{
sum_tag[o]=sum_tag[lson]+sum_tag[rson];
maxright_tag[o]=max(maxright_tag[lson]+sum_tag[rson],maxright_tag[rson]);
}
void update(int o,int L,int R,int pos,int val)
{
if(L==R)
{
sum_tag[o]=maxright_tag[o]=val;
return;
}
int M=(L+R)>>1;
if(pos<=M) update(lson,L,M,pos,val);
else update(rson,M+1,R,pos,val);
pushup(o);
}
int cnt=0;
struct Interval
{
int L,R,id;
Interval(){}
Interval(int _L,int _R,int _id):L(_L),R(_R),id(_id){}
}intervals[MAXN];
void getseg(int o,int L,int R,int ql,int qr)
{
if(ql==L&&qr==R)
{
intervals[++cnt]=Interval(L,R,o);
return;
}
int M=(L+R)>>1;
if(qr<=M) getseg(lson,L,M,ql,qr);
else if(ql>M) getseg(rson,M+1,R,ql,qr);
else
{
getseg(lson,L,M,ql,M);
getseg(rson,M+1,R,M+1,qr);
}
}
int BinarySearch(int o,int L,int R,int sum) //在[L,R]的节点o中找最靠右边的i,使得后缀[i,tot]中的元素和>0,(R,tot]这段区间内的元素和已知为sum
{
while(L!=R)
{
int M=(L+R)>>1;
if(sum+maxright_tag[rson]>0) o=rson,L=M+1;
else
{
sum+=sum_tag[rson];
o=lson;
R=M;
}
}
return L;
}
int main()
{
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
char s[10];
int q,n=0,tot=0;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%s",s);
if(s[1]=='u') //1:push
{
cmd[i]=1;
scanf("%d%d",&num[i],&t[i]);
}
else if(s[1]=='o') //2:pop
{
cmd[i]=2;
scanf("%d",&t[i]);
}
else //3:peak
{
cmd[i]=3;
scanf("%d",&t[i]);
}
data[tot++]=t[i];
}
sort(data,data+tot); //时间离散化
for(int i=1;i<=q;i++) t[i]=1+lower_bound(data,data+tot,t[i])-data;
for(int i=1;i<=q;i++)
{
if(cmd[i]==1) //1:push
{
update(1,1,tot,t[i],1);
val[t[i]]=num[i];
}
else if(cmd[i]==2) //2:pop
update(1,1,tot,t[i],-1);
else
{
cnt=0;
bool flag=false;
getseg(1,1,tot,1,t[i]);
int sum=0; //sum=当前的后缀和
for(int j=cnt;j>=1;j--)
{
if(sum+maxright_tag[intervals[j].id]>0)
{
printf("%d\n",val[BinarySearch(intervals[j].id,intervals[j].L,intervals[j].R,sum)]);
flag=true;
break;
}
sum+=sum_tag[intervals[j].id]; //!!!!!
}
if(!flag) printf("-1\n");
}
}
return 0;
}