https://www.luogu.com.cn/problem/P1198
题目描述
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:LL不超过当前数列的长度。(L > 0)(L>0)
2、 插入操作。
语法:A n
功能:将nn加上tt,其中tt是最近一次查询操作的答案(如果还未执行过查询操作,则t=0t=0),并将所得结果对一个固定的常数DD取模,将所得答案插入到数列的末尾。
限制:nn是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入格式
第一行两个整数,MM 和 DD,其中 MM 表示操作的个数,DD 如上文中所述。
接下来的 MM 行,每行一个字符串,描述一个具体的操作。语法如上文所述。
输出格式
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
输入输出样例
输入 #1复制
5 100 A 96 Q 1 A 97 Q 1 Q 2
输出 #1复制
96 93 96
说明/提示
数据规模与约定
对于全部的测试点,保证 1 \leq M \leq 2 \times 10^51≤M≤2×105,1 \leq D \leq 2 \times 10^91≤D≤2×109。
思路:线段树板子题
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e5+10;
typedef long long LL;
LL a[maxn];
LL M,D;
struct SegmengTree
{
LL l,r,val;//val为区间最大数
#define l(x) tree[x].l
#define r(x) tree[x].r
#define val(x) tree[x].val
}tree[maxn*4];
void build(LL p,LL l,LL r)
{
l(p)=l;r(p)=r;
if(l(p)==r(p)) {val(p)=a[l];return;}
LL mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
val(p)=max(val(p*2),val(p*2+1));
}
void change(LL p,LL l,LL r,LL t)
{
if(l<=l(p)&&r>=r(p)) {val(p)+=t;return;}
LL mid=(l(p)+r(p))/2;
if(l<=mid) change(p*2,l,r,t);
if(r>mid) change(p*2+1,l,r,t);
val(p)=max(val(p*2),val(p*2+1));
}
LL ask(LL p,LL l,LL r)
{
if(l<=l(p)&&r>=r(p)) {return val(p);}
LL mid=(l(p)+r(p))/2;
LL ans=-0x3f3f3f3f;
if(l<=mid) ans=max(ans,ask(p*2,l,r));
if(r>mid) ans=max(ans,ask(p*2+1,l,r));
return ans;
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
build(1,1,2e5);
cin>>M>>D;
char op[10];LL x;LL cnt=0;
LL length=0;
for(LL i=1;i<=M;i++)
{
cin>>op>>x;
if(op[0]=='A')
{
length++;
x=(x%D+cnt%D)%D;
change(1,length,length,x);
}
else if(op[0]=='Q')
{
LL p=ask(1,length-x+1,length);
cnt=p;
cout<<p<<endl;
}
}
return 0;
}