题意:
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:L不超过当前数列的长度。(L>=0)
2、 插入操作。
语法:A n
功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。
限制:n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
本题可以用线段树做(在洛谷上能过,但是我的代码在bzoj会T),似乎还可以单调队列甚至分块。我目前写的线段树都是传统的写法,不是zkw线段树。查询就是区间求区间[n-l+1,n]的最大值。记录一下上一个插入操作的返回值,然后插入相当于单点修改。那么考虑一下建树,发现在一开始我们不知道每个点的权值,所以我们只需要建一棵空树,建空树的目的是确定每个节点的左右子节点的编号。本题有一个小细节:当一开始数列里没有数的时候也会询问你最大数值,这时候要特判一下,输出0
下面发一下在洛谷AC的代码:(此代码直接交到bzoj会RE,原因尚不清楚)
#include <bits/stdc++.h>
using namespace std;
int m,d;
char s;
int ji,cnt;
struct node
{
int r,l;
int maxn,add;
}tr[4000050];
void build(int rt,int l,int r)
{
tr[rt].l=l,tr[rt].r=r;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
return;
}
void pushdown(int rt)
{
if(tr[rt].add)
{
tr[rt<<1].add+=tr[rt].add;
tr[rt<<1|1].add+=tr[rt].add;
tr[rt<<1].maxn+=tr[rt].add;
tr[rt<<1|1].maxn+=tr[rt].add;
tr[rt].add=0;
}
return;
}
void update(int rt,int le,int ri,long long x)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
{
tr[rt].maxn=tr[rt].maxn+x;
tr[rt].add+=x;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(le<=mid)
update(rt<<1,le,ri,x);
if(ri>mid)
update(rt<<1|1,le,ri,x);
tr[rt].maxn=max(tr[rt<<1].maxn,tr[rt<<1|1].maxn);
return;
}
int query(int rt,int le,int ri)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&ri>=r)
return tr[rt].maxn;
pushdown(rt);
int res=-2147483647;
int mid=(l+r)>>1;
if(le<=mid)
res=query(rt<<1,le,ri);
if(ri>mid)
res=max(res,query(rt<<1|1,le,ri));
if(res==-2147483647)//特判l=0
return 0;
return res;
}
int main()
{
scanf("%d%d",&m,&d);
build(1,1,200000);
for(int i=1;i<=m;i++)
{
long long x;
cin>>s>>x;
if(s=='Q')
{
ji=query(1,cnt-x+1,cnt);
printf("%d\n",ji);
}
if(s=='A')
{
cnt++;
update(1,cnt,cnt,(x+ji)%d);
}
}
return 0;
}