题目
有个小迷糊对栈进行了操作,
但他现在回想起来第p[i]次操作干了什么,是入栈还是出栈
特别地,如果对空栈pop,什么也不会发生
但是并不是按顺序回想起来的,
问按他回想的前i次对栈进行操作,栈顶的元素是什么
思路来源
https://www.cnblogs.com/quintessence/p/6392073.html
题解
本来是线段树维护后缀和,push对p[i]点+1,pop对p[i]点-1
然后找到第一个后缀和大于0的点就是答案
但是考虑到有些栈的操作是无效的,所以不能用线段树维护和
线段树维护区间和,每个点只代表自己的值
线段树维护最大值,每个点都代表一个后缀和,所以更新时就是一波区间修改
每个点都代表一个后缀和,就是每个点都不能影响其后面的点
所以每次修改p[i]的时候只能对[1,p[i]]的区间进行区间修改
注意到如果有pop操作无效,一定是pop在push前,
这样后来的push的区间修改会令自己这个点+1,先向右查询时并不影响答案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int m;
int pos,op,v;
int ans[maxn];
int dat[maxn*5],cov[maxn*5];
void pushup(int p)
{
dat[p]=max(dat[p<<1],dat[p<<1|1]);
//其实是维护这两段和是否大于0
//正+正>0,负+负<0 如果两段和>0一定是大正+小负
}
void pushdown(int p,int l,int r)
{
if(cov[p])
{
int mid=(l+r)/2;
dat[p<<1]+=cov[p];
dat[p<<1|1]+=cov[p];
cov[p<<1]+=cov[p];
cov[p<<1|1]+=cov[p];
cov[p]=0;
}
}
void update(int p,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr)
{
dat[p]+=v;
cov[p]+=v;
return;
}
pushdown(p,l,r);
int mid=(l+r)/2;
if(ql<=mid)update(p<<1,l,mid,ql,qr,v);
if(qr>mid)update(p<<1|1,mid+1,r,ql,qr,v);
pushup(p);
}
int ask(int p,int l,int r)
{
if(l==r)return dat[p]>0?ans[l]:-1;
pushdown(p,l,r);
int mid=(l+r)/2;
if(dat[p<<1|1]>0)return ask(p<<1|1,mid+1,r);
if(dat[p<<1]>0)return ask(p<<1,l,mid);
return -1;
}
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&pos,&op);
if(op)
{
scanf("%d",&v);
ans[pos]=v;
update(1,1,m,1,pos,1);
}
else update(1,1,m,1,pos,-1);
printf("%d\n",ask(1,1,m));
}
return 0;
}