在如今的网络中,TCP 是一种被广泛使用的网络协议,它在传输层提供了可靠的通信服务。
众所周知,网络是存在时延的,例如用户先后向服务器发送了两个指令 op1 和 op2 ,并且希
望服务器先处理指令 op1 ,再处理指令 op2 ;但由于网络时延,这两个指令可能会失序到达,
而导致服务器先执行了指令 op2 ,这是我们不希望看到的。TCP 协议拥有将失序到达的报文
按顺序重组的功能,一种方法是给每一个报文打上一个时间戳。而你今天要实现的功能比这
个要简单很多。我们需要你维护一个服务器,这个服务器的功能是一个简单的栈,你会接收
三种用户的指令:
push x t — 表示将 x 元素入栈,这条指令的时间戳为 t
pop t — 表示将栈顶元素弹出,这条指令的时间戳为 t
peak t — 用户询问现在栈顶元素的值,这条指令的时间戳为 t
当一条时间戳为 t 的指令到达时,你需要进行如下处理:
1.将所有之前执行的时间戳大于 t 的 push 和 pop 指令全部撤销
2.执行当前这条指令
3.按时间戳顺序重新执行在第 1 步被撤销的指令
注意你不需要撤销以及重新执行之前已经执行过的 peak 指令,也就是说每一条 peak 指令只
有在它到达的时候会被执行一次。
我们保证每一条指令的时间戳都是唯一的;若你在需要执行一条 pop 指令时发现当前栈为
空,则当前你可以忽略这条指令。
第一行包含一个整数 n ,表示指令总数。
接下来 n 行按指令到达服务器的顺序给出每一条指令,有三种类型
push x t
pop t
peak t
对于每一条 peak 指令,输出对应的答案占一行;若栈为空,输出 −1 。
样例输入:
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 <= n <= 80000;
对于 100%的数据,1 <= n <= 300000,0 <= x,t <= 1000000000。
虽然题目非常绕口,但是可以归纳为:在查询时间 t 的栈顶元素时,从头开始离线对所有时刻小于等于 t 的所有操作全部进行一遍。
那么我们可以先对所有的 t 离散化以减少空间消耗,然后维护一个线段树,线段树上的序列下标对应的就是时间点,线段树上某个位置 t 的值为1,表示此刻有个元素入栈,并标记这个时刻入栈的是什么元素,-1表示此刻进行了一次出栈操作。因为题目要求,因此对于 i>j 的两个操作 i,j ,若它们都是在同一时刻,那么用新的 i 覆盖序列中旧的 j 。如果维护一个值 x 代表当前时刻 t 栈中元素个数的话,初始 x 为0,从1到 t 对-1 0 1序列扫一遍,若某时 x=0 则不能做-1,否则加上序列的当前位置的元素。这样就能得到 x ,而且也能想到,时刻 t 时栈顶的元素是最大的 i<t ,使得-1 0 1的序列上的区间 [i,t] 中区间和 >0 。
容易想到在线段树上维护区间和标记 sum[] ,一个比较好做的做法就是二分 i ,每次对区间求和然后修改上下界,这样的做法是 O(qlog2n) 的,由于此题很坑比地卡 log ,因此是不能用这种做法的。
然后考虑更快的做法:维护线段树上的区间内最大后缀和标记 maxright ,将 [1,t] 这段区间,拆分成尽量少的线段树结点,然后从最右边那个结点开始暴力扫,维护当前扫过的结点对应区间和 sum ,若扫到结点 j ,且 maxright[j]+sum>0 ,则说明我们要找的 i 就在结点 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;
}