题目链接
题意:给你一个初始状态为空的栈和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
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&&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
如果感觉线段树的方法不大好想,也可以考虑采用分块的方法,只比线段树的方法慢了400ms:
#include
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=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