版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/83108051
题意:给你一个初始状态为空的栈和n个操作,操作共有三种:
1.向栈中加入一个元素(push)
2.清除栈顶元素(pop)
3.询问栈顶元素(peak)
每个操作都有一个时间戳,在执行每个操作之前,都需要把所有时间戳在该操作之前的操作全都撤销,然后执行该操作,并重做之前的操作(peak不需要重做)。
解法:构造一棵线段树,每个区间需要维护两个值:sum(区间和),maxrsum(区间最大后缀和。首先对所有的时间戳离散化,对于每个push操作,将该操作对应的时间戳的位置上的元素+1,对于pop操作则-1,对于peak操作,从时间戳往前第一个到该时间戳的后缀大于0的时间点就是栈顶元素被push进的时间点。
区间最大后缀和维护公式:maxrsum[o]=max(maxrsum[o<<1]+sum[o<<1|1],maxrsum[o<<1|1])
询问的过程略为复杂,要从后往前枚举每个子区间,对于访问过的区间还要对区间和进行累加,便于前面区间的后缀和计算。
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int INF=0x3f3f3f3f;
struct query
{
int type,num,t;
} qr[N];
int n,order[N],val[N],tt,kase=0;
struct SEGTREE
{
static const int N=2e5+10;
int sum[N],maxrsum[N];
void init()
{
memset(sum,0,sizeof sum);
memset(maxrsum,0,sizeof maxrsum);
}
void add(int pos,int x,int o=1,int l=0,int r=n-1)
{
if(l==r)
{
sum[o]+=x;
maxrsum[o]=x;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)add(pos,x,o<<1,l,mid);
else add(pos,x,o<<1|1,mid+1,r);
sum[o]=sum[o<<1]+sum[o<<1|1];
maxrsum[o]=max(maxrsum[o<<1]+sum[o<<1|1],maxrsum[o<<1|1]);
}
int askans(int L,int R,int o=1,int l=0,int r=n-1)
{
if(l>R||r<L)return -1;
if(l>=L&&r<=R&&maxrsum[o]+tt<=0)
{
tt+=sum[o];
return -1;
}
if(l==r)return l;
int t;
int mid=(l+r)>>1;
if(~(t=askans(L,R,o<<1|1,mid+1,r)))return t;
return askans(L,R,o<<1,l,mid);
}
} segtree;
int main()
{
while(scanf("%d",&n)&&n)
{
printf("Case #%d:\n",++kase);
segtree.init();
char buf[10];
for(int i=0; i<n; ++i)
{
scanf("%s",buf);
if(buf[1]=='u')
{
qr[i].type=1;
scanf("%d%d",&qr[i].num,&qr[i].t);
}
else if(buf[1]=='o')
{
qr[i].type=2;
scanf("%d",&qr[i].t);
}
else if(buf[1]=='e')
{
qr[i].type=3;
scanf("%d",&qr[i].t);
}
order[i]=qr[i].t;
}
sort(order,order+n);
for(int i=0; i<n; ++i)qr[i].t=lower_bound(order,order+n,qr[i].t)-order;
for(int i=0; i<n; ++i)
{
if(qr[i].type==1)
{
segtree.add(qr[i].t,1);
val[qr[i].t]=qr[i].num;
}
else if(qr[i].type==2)
{
segtree.add(qr[i].t,-1);
}
else if(qr[i].type==3)
{
tt=0;
int ans=segtree.askans(0,qr[i].t);
printf("%d\n",~ans?val[ans]:-1);
}
}
}
return 0;
}
如果感觉线段树的方法不大好想,也可以考虑采用分块的方法,只比线段树的方法慢了400ms:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int INF=0x3f3f3f3f;
struct query
{
int type,num,t;
} qr[N];
int n,order[N],val[N],tt,kase=0;
struct BLOCK
{
static const int N=5e4+10;
static const int SQRTN=sqrt(N)+10;
int rsum[N],sum[SQRTN],maxrsum[SQRTN],in[N];
void init()
{
memset(sum,0,sizeof sum);
memset(rsum,0,sizeof rsum);
memset(maxrsum,0,sizeof maxrsum);
}
void getin()
{
int sz=sqrt(n+0.5);
for(int i=0; i<n; ++i)in[i]=i/sz;
}
void add(int pos,int x)
{
sum[in[pos]]+=x;
maxrsum[in[pos]]=~INF;
int l=lower_bound(in,in+n,in[pos])-in;
int r=upper_bound(in,in+n,in[pos])-in-1;
for(int i=l; i<=pos; ++i)rsum[i]+=x;
for(int i=l; i<=r; ++i)maxrsum[in[i]]=max(maxrsum[in[i]],rsum[i]);
}
int askans(int R)
{
int l=lower_bound(in,in+n,in[R])-in;
int r=upper_bound(in,in+n,in[R])-in-1;
int tmp=r==R?0:rsum[R+1];
for(int i=R; i>=l; --i)if(rsum[i]-tmp>0)return i;
tmp=sum[in[R]]-tmp;
for(int u=in[R]-1; u>=0; --u)
{
if(maxrsum[u]+tmp<=0)
{
tmp+=sum[u];
continue;
}
l=lower_bound(in,in+n,u)-in;
r=upper_bound(in,in+n,u)-in-1;
for(int i=r; i>=l; --i)if(rsum[i]+tmp>0)return i;
}
return -1;
}
} block;
int main()
{
while(scanf("%d",&n)&&n)
{
printf("Case #%d:\n",++kase);
block.init();
block.getin();
char buf[10];
for(int i=0; i<n; ++i)
{
scanf("%s",buf);
if(buf[1]=='u')
{
qr[i].type=1;
scanf("%d%d",&qr[i].num,&qr[i].t);
}
else if(buf[1]=='o')
{
qr[i].type=2;
scanf("%d",&qr[i].t);
}
else if(buf[1]=='e')
{
qr[i].type=3;
scanf("%d",&qr[i].t);
}
order[i]=qr[i].t;
}
sort(order,order+n);
for(int i=0; i<n; ++i)qr[i].t=lower_bound(order,order+n,qr[i].t)-order;
for(int i=0; i<n; ++i)
{
if(qr[i].type==1)
{
block.add(qr[i].t,1);
val[qr[i].t]=qr[i].num;
}
else if(qr[i].type==2)
{
block.add(qr[i].t,-1);
}
else if(qr[i].type==3)
{
tt=0;
int ans=block.askans(qr[i].t);
printf("%d\n",~ans?val[ans]:-1);
}
}
}
return 0;
}