BZOJ 1012 [JSOI2008]最大数maxnumber

1012: [JSOI2008]最大数maxnumber

Time Limit: 3 Sec  Memory Limit: 162 MB
Submit: 5425  Solved: 2397
[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如上文中所述,满足(0

Output

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

Sample Input

5 100
A 96
Q 1
A 97
Q 1
Q 2

Sample Output

96
93
96

HINT

 

Source

题解:先写个线段树拿分再说。不过我的处理方式很奇怪?不过幸好常数小。。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 #define CH for(int d=0;d<2;d++) if(ch[d])
10 #define lson x->ch[0],L,M
11 #define rson x->ch[1],M+1,R
12 using namespace std;
13 const int maxn=200000+10,maxnode=400000+10,inf=-1u>>1;
14 int mod,last,ql,qr,pos,cv,_mx,n,len=0;
15 struct node{
16     node*ch[2];int mx;node(){mx=-inf;}
17     void update(){mx=-inf;CH{if(ch[d]->mx>mx)mx=ch[d]->mx;}return;}
18 }seg[maxnode],*nodecnt=seg,*root;
19 void build(node*&x=root,int L=1,int R=n){
20     x=nodecnt++;if(L==R)return;int M=L+R>>1;build(lson);build(rson);return;
21 }
22 void update(node*&x,int L,int R){
23     if(L==R){x->mx=cv;return;}int M=L+R>>1;
24     if(pos<=M)update(lson);else update(rson);x->update();
25 }
26 void query(node*x,int L,int R){
27     if(ql<=L&&R<=qr){_mx=max(_mx,x->mx);return;}int M=L+R>>1;
28     if(ql<=M)query(lson);if(qr>M)query(rson);return;
29 }
30 inline int read(){
31     int x=0,sig=1;char ch=getchar();
32     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();}
33     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar();
34     return x*sig;
35 }
36 inline void write(int x){
37     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
38     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
39     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return;
40 }
41 inline char readc(){
42     char ch=getchar();for(;!isalpha(ch);ch=getchar());return ch;
43 }
44 void init(){
45     n=read();mod=read();build();
46     for(int i=1;i<=n;i++){
47         if(readc()=='A')cv=(last+read())%mod,len++,pos=len,update(root,1,n);
48         else{
49             _mx=-inf;ql=len-read()+1;qr=len;query(root,1,n);write(_mx);ENT;last=_mx;
50         }
51     }
52     return;
53 }
54 void work(){
55     return;
56 }
57 void print(){
58     return;
59 }
60 int main(){
61     init();work();print();return 0;
62 }

当然这道题还有一个神奇的做法:观察到如果一个数出现在某个数后边,并且这个数大于之前的数,那么之前的数无论如何也不会成为最大的数的。

所以我们可以维护一个单调的东西。然后就没有然后了。

还有,以后别写二分了!!!!!!!

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 using namespace std;
10 const int maxn=200000+10,inf=-1u>>1;
11 int s[maxn],top=0,n,mod,v[maxn],len=0,last;
12 inline int read(){
13     int x=0,sig=1;char ch=getchar();
14     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();}
15     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar();
16     return x*=sig;
17 }
18 inline void write(int x){
19     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
20     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
21     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return;
22 }
23 inline char readc(){
24     char ch=getchar();for(;!isalpha(ch);ch=getchar());return ch;
25 }
26 void init(){
27     n=read();mod=read();
28     for(int i=1;i<=n;i++){
29         if(readc()=='A'){
30             int cv=(read()+last)%mod;  
31             v[++len]=cv;
32             while(top&&v[s[top]]<=cv)top--;  
33             s[++top]=len;
34         } else {
35             int pos=lower_bound(s+1,s+top+1,len-read()+1)-s;
36             write(last=v[s[pos]]);ENT;
37         }
38     }
39     return;
40 }
41 void work(){
42     return;
43 }
44 void print(){
45     return;
46 }
47 int main(){
48     init();work();print();return 0;
49 }

GYH神犇还提出了离线RMQ的思想。可以发现,每一次插入数即为新建一个集合,而删除一个数即将两集合合并,每次询问则为查询一个数所在的集合的代表元素。这正好是并查集所支持的各种操作。而并查集的每次操作的均摊复杂度接近O(1),所以RMQ的离线问题也可以在O(N+Q)的时间内解决,空间复杂度也是O(N+Q)。

但是。。。这个跟普通的二分跑得差不多,不过代码复杂度少了很多。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 using namespace std;
10 const int maxn=200000+10;
11 int stack[maxn],top=0,fa[maxn],A[maxn];
12 int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}
13 inline int read(){
14     int x=0,sig=1;char ch=getchar();
15     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();}
16     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar();
17     return x*=sig;
18 }
19 inline void write(int x){
20     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
21     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
22     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return;
23 }
24 inline char readc(){
25     char ch=getchar();while(!isalpha(ch))ch=getchar();return ch;
26 }
27 void init(){
28     int last=0,m,mod,x,len=0;
29     m=read();mod=read();
30     for(int i=1;i<=m;i++) fa[i]=i;
31     while(m--){
32         if(readc()=='Q')x=(len-read()),write(last=A[find(x)]),ENT;
33         else{
34             A[len]=(last+read())%mod;
35             while(top&&A[stack[~-top]]<=A[len])fa[stack[--top]]=len;
36             stack[top++]=len++;
37         }
38     }
39     return;
40 }
41 void work(){
42     return;
43 }
44 void print(){
45     return;
46 }
47 int main(){init();work();print();return 0;}

 

posted @ 2015-07-14 09:58  AI_Believer  阅读(259)  评论(6编辑  收藏  举报