题目描述
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:L不超过当前数列的长度。(L > 0)
2、 插入操作。
语法:A n
功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。
限制:n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入格式
第一行两个整数,M和 D,其中 M 表示操作的个数,D如上文中所述。
接下来的 M 行,每行一个字符串,描述一个具体的操作。语法如上文所述。
输出格式
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
样例输入输出
输入
5 100
A 96
Q 1
A 97
Q 1
Q 2
输出
96
93
96
分析
这题我选用线段树做法,效率还是可以。求的是一个区间最大值,用一个max函数去打擂台比较,然后套入线段树的模板函数就完事。
注意:mx域一开始赋值为-1e16,为了确保比大小的时候不被一些负数刷掉。然后最后询问的答案要用一个变量存起来,下次碰到修改函数的时候就可以直接用,比较方便。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
struct node
{
int l,r;
ll mx;
}tree[1000001];
int n,len;
char num[5];
ll m,v,p;
void build(int k,int l,int r)//建树,先建好树再做修改操作
{
tree[k].l=l;
tree[k].r=r;
tree[k].mx=-1e16;//赋值成最小
if(l==r) return;
int mid=(l+r)/2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
}
void move(int dep,int f,ll k)//在树上修改的函数,与build加起来等于之前的insert
{
if(tree[dep].l==tree[dep].r&&tree[dep].l==f)
{
tree[dep].mx=k;
return;
}
int mid=(tree[dep].l+tree[dep].r)/2;
if(f<=mid)
{
move(dep*2,f,k);
}
else
{
move(dep*2+1,f,k);
}
tree[dep].mx=max(tree[dep*2].mx,tree[dep*2+1].mx);
}
ll ask(int k,int l,int r)
{
if(tree[k].l==l&&tree[k].r==r)//已经是叶子节点
{
return tree[k].mx;
}
int mid=(tree[k].l+tree[k].r)/2;//二分
if(r<=mid)
{
return ask(k*2,l,r);
}
else if(l>mid)
{
return ask(k*2+1,l,r);
}
return max(ask(k*2,l,mid),ask(k*2+1,mid+1,r));//找最大
}
int main()
{
cin>>n>>m;
build(1,1,n);
for(int i=1;i<=n;i++)
{
scanf("%s%lld",&num,&v);
if(num[0]=='A')
{
v=(v+p)%m;//计算
move(1,++len,v);
}
else
{
p=ask(1,len-v+1,len);//p存了上一次的答案
cout<<p<<endl;
}
}
return 0;
}