Description
现在请求你维护一个数列,要求提供以下两种操作:1、 查询操作。语法:Q L 功能:查询当前数列中末尾L
个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。2、 插入操作。语法:A n 功能:将n加
上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取
模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。注意:初始时数列是空的,没有一个
数。
Input
第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足D在longint内。接下来
M行,查询操作或者插入操作。
Output
对于每一个询问操作,输出一行。该行只有一个数,即序列中最后L个数的最大数。
Sample Input
5 100
A 96
Q 1
A 97
Q 1
Q 2
A 96
Q 1
A 97
Q 1
Q 2
Sample Output
96
93
96
93
96
题解:
这题算是比较经典的一道线段树题了。看到各路神犇拿各种niubi算法在代码量和常数上随便吊打线段树,我还是顶住压力写了这一篇线段树题解。
首先我们把线段树建出来,建一棵m大小的线段树。
然后每次'A'操作就是把空白的末尾部位填充要填充的数。
每次'Q'操作就是求末尾长度为l的区间最小值。
这两个操作都可以直接由最基本的线段树完成。
AC代码:
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
const int Maxn=200005;
int m,p;
struct node{
ll x,c;
int l,r;
}t[Maxn*4];
inline int lson(int rt){
return rt*2;
}
inline int rson(int rt){
return rt*2+1;
}
inline ll mx(ll x,ll y){
return x>y?x:y;
}
inline void pushup(int rt){
t[rt].x=mx(t[lson(rt)].x,t[rson(rt)].x);
}
inline void build(int l,int r,int rt){
t[rt].l=l;t[rt].r=r;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,lson(rt));
build(mid+1,r,rson(rt));
}
inline void update(int l,ll x,int rt){
if(t[rt].l==l&&t[rt].r==l){
t[rt].x=x;
return;
}
int mid=t[rt].l+t[rt].r>>1;
if(l<=mid)update(l,x,lson(rt));
else update(l,x,rson(rt));
pushup(rt);
}
inline ll query(int l,int r,int rt){
if(t[rt].l>=l&&t[rt].r<=r)return t[rt].x;
int mid=t[rt].l+t[rt].r>>1;ll ans=-1000000000;
if(l<=mid)ans=mx(ans,query(l,r,lson(rt)));
if(r>mid)ans=mx(ans,query(l,r,rson(rt)));
return ans;
}
int main(){
char inc;ll u,t=0;int cnt=0;
scanf("%d%d\n",&m,&p);
build(1,m,1);
while(m--){
scanf("%c %lld\n",&inc,&u);
if(inc=='A'){
cnt++;
update(cnt,(u+t)%p,1);
}
else{
t=query(cnt-u+1,cnt,1);
printf("%lld\n",t);
}
}
return 0;
}