描述
http://www.lydsy.com/JudgeOnline/problem.php?id=1012
两种操作:
1.求序列末尾n个数中的最大值.
2.在序列末尾插入一个数.
分析
线段树求RMQ裸题,不离线也可以做.
我们来说说怎么用树状数组求RMQ.
求区间和值的树状数组中c[i]表示的是区间(i-lowbit(i)+1,c[i])的和值,我们现在让它表示这个区间的最大值.
1.查询操作get_max(l,r).
如果l<=r-lowbit(r)+1.那么c[r]表示的区间全部在(l,r)内部,于是就ret=max(ret,c[r]),r-=lowbit(r).
否则,ret=max(ret,num[r]),r--.
2.插入操作.update(id,x).
求出区间M=(id-lowbit(id),id)的最大值,c[id]=max(M,num[id]).
这样只会用到查询操作,画图可知,每次查询"上去","下来"最多是2logn,所以复杂度是\(O(logn)\)的.
线段树:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=200000+5; 5 struct node{ 6 int l,r,m; 7 }a[maxn<<2]; 8 int m,d,t,n,cnt; 9 char str[3]; 10 void build(int l,int r,int k){ 11 a[k].l=l; a[k].r=r; 12 if(l==r) return; 13 int mid=l+(r-l)/2; 14 build(l,mid,k<<1); build(mid+1,r,k<<1|1); 15 } 16 void update(int id,int x,int k){ 17 if(a[k].l==a[k].r){ 18 a[k].m=x; 19 return; 20 } 21 int mid=a[k].l+(a[k].r-a[k].l)/2; 22 if(id<=mid) update(id,x,k<<1); 23 else update(id,x,k<<1|1); 24 a[k].m=max(a[k<<1].m,a[k<<1|1].m); 25 } 26 int get_max(int l,int r,int k){ 27 if(a[k].l==l&&a[k].r==r) return a[k].m; 28 int mid=a[k].l+(a[k].r-a[k].l)/2; 29 if(r<=mid) return get_max(l,r,k<<1); 30 else if(l>mid) return get_max(l,r,k<<1|1); 31 else return max(get_max(l,mid,k<<1),get_max(mid+1,r,k<<1|1)); 32 } 33 int main(){ 34 scanf("%d%d",&m,&d); 35 build(1,m,1); 36 while(m--){ 37 scanf("%s%d",str,&n); 38 if(str[0]=='A'){ 39 cnt++; 40 update(cnt,(t+n)%d,1); 41 } 42 else printf("%d\n",t=get_max(cnt-n+1,cnt,1)); 43 } 44 return 0; 45 }
树状数组:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=200000+5; 5 int m,d,n,t,cnt; 6 int a[maxn],c[maxn]; 7 char str[3]; 8 inline int lowbit(int x){ return x&-x; } 9 int get_max(int l,int r){ 10 if(r<l) return 0; 11 int ret=a[r]; 12 while(l<=r){ 13 if(r-lowbit(r)+1>=l){ 14 ret=max(ret,c[r]); 15 r-=lowbit(r); 16 } 17 else ret=max(ret,a[r]), r--; 18 } 19 return ret; 20 } 21 int main(){ 22 scanf("%d%d",&m,&d); 23 while(m--){ 24 scanf("%s%d",str,&n); 25 if(str[0]=='A'){ 26 a[++cnt]=(n+t)%d; 27 c[cnt]=max(get_max(cnt-lowbit(cnt)+1,cnt-1),a[cnt]); 28 } 29 else{ 30 printf("%d\n",t=get_max(cnt-n+1,cnt)); 31 } 32 } 33 return 0; 34 }
1012: [JSOI2008]最大数maxnumber
Time Limit: 3 Sec Memory Limit: 162 MBSubmit: 7678 Solved: 3313
[Submit][Status][Discuss]
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
A 96
Q 1
A 97
Q 1
Q 2
Sample Output
93
96
HINT
数据如下http://pan.baidu.com/s/1i4JxCH3