洛谷P1198 [JSOI2008]最大数(线段树)
题目描述
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:LLL不超过当前数列的长度。(L>0)(L > 0)(L>0)
2、 插入操作。
语法:A n
功能:将nnn加上ttt,其中ttt是最近一次查询操作的答案(如果还未执行过查询操作,则t=0t=0t=0),并将所得结果对一个固定的常数DDD取模,将所得答案插入到数列的末尾。
限制:nnn是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入格式
第一行两个整数,MMM和DDD,其中MMM表示操作的个数(M≤200,000)(M \le 200,000)(M≤200,000),DDD如上文中所述,满足(0<D<2,000,000,000)(0<D<2,000,000,000)(0<D<2,000,000,000)
接下来的MMM行,每行一个字符串,描述一个具体的操作。语法如上文所述。
输出格式
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
输入输出样例
输入 #1
5 100 A 96 Q 1 A 97 Q 1 Q 2
输出 #1
96 93 96
线段树裸题。有这么几个需要注意的地方:
1.要错误理解当前序列最右侧与整个线段树最右侧叶子节点的关系!事先建树时是按照最大规模建的,所以在不断加入点的过程中序列最右端对应的叶子节点实际上是在线段树的最左与最右边叶子节点之内的。
2.INF一定要足够大,0x3f3f3f3f3f3f3f3f可以保证不爆。
3.对于Q,A若用scanf读取最好是%s,不然的话处理换行符会比较啰嗦。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; int M,D; long long length=0; struct SegmentTree { int l; int r; long long val; }t[800005]; void build(int k,int l,int r) { t[k].val=-INF; t[k].l=l; t[k].r=r; if(l==r) { return; } int mid=(l+r)/2; build(2*k,l,mid); build(2*k+1,mid+1,r); } long long query(int k,int l,int r,int x,int y)//k为当前节点编号,l为当前节点覆盖区间左端点,r为右端点,x是操作区间左端点,y为操作区间右端点 { if(l>=x&&r<=y)return t[k].val; int mid=(l+r)/2; long long ans=-INF; //赋值为负无穷 if(mid>=x) { ans=query(2*k,l,mid,x,y); } if(y>mid)ans=max(ans,query(2*k+1,mid+1,r,x,y));//线段树常见套路 return ans; } void add(int k,int l,int r,int y,int num)//num为操作数,其他类似 { if(l==r) { t[k].val=num; return; } int mid=(l+r)/2; if(y<=mid)add(2*k,l,mid,y,num);//y代表了单点操作的点的位置。这个题y即为当前序列最右边的位置 。注意!!不要错误理解当前序列最右侧与整个线段树最右侧叶子节点的关系!事先建树时是按照最大规模建的,所以在不断加入点的过程中序列最右端对应的叶子节点实际上是在线段树的叶子节点之内的 else add(2*k+1,mid+1,r,y,num); t[k].val=max(t[2*k].val,t[2*k+1].val);//不要忘记向上更新 } int main() { cin>>M>>D; long long last=0; build(1,1,200005); while(M--) { char c[3]; long long temp; scanf("%s%lld",&c,&temp); if(c[0]=='A') { length++; add(1,1,200000,length,(temp+last)%D);// } else if(c[0]=='Q') { long long ans=query(1,1,200000,length-temp+1,length); last=ans; printf("%lld\n",ans); } } return 0; }