标签:maxnumber 洛谷 数列 最大数 int 查询 队列 ans st
Author : hiang
Time Limit: 3 Sec Memory Limit: 162 MB
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
5 100A 96
Q 1
A 97
Q 1
Q 2
Sample Output
9693
96 这里提供四种思路: 1.线段树 2.ST表 3.单调队列+并查集 4.树状数组 一、线段树 这道题虽然可以用线段树的思路来做,但其实连建树都不用,因为一开始数列中并没有数,只需要在数列末尾加数,并查询已有数列即可,所以我只用了一个数组来存储区间最大值。 注:本题洛谷的数据非常变态,数据加了全是负数和L为0的情况,所以wa了无数发....以下代码是bzoj的AC代码,本代码在洛谷提交的话会全部MLE,需要把char改成字符数组,具体原因不详。 AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const long long inf=2*1e9; 4 #define LL long long 5 LL d,val[800005]; 6 int m,ans;//ans为数组元素个数 7 char c; 8 void add(int p,int ll,int rr,int x,LL k) 9 {//p为下标,ll,rr为p对应区间的左右端点 10 if(ll==rr) 11 { 12 val[p]=k; 13 return; 14 } 15 int mid=(ll+rr)>>1; 16 if(x<=mid) 17 add(p<<1,ll,mid,x,k); 18 if(x>mid) 19 add(p<<1|1,mid+1,rr,x,k); 20 val[p]=max(val[p<<1],val[p<<1|1]); 21 } 22 LL query(int p,int pl,int pr,int ll,int rr) 23 {//pl,pr为p对应区间左右端点,ll,rr为要查询的区间左右端点 24 LL maxx=-inf; 25 if(pl>=ll&&pr<=rr) 26 return val[p]; 27 int mid=(pl+pr)>>1; 28 if(ll<=mid) 29 maxx=max(maxx,query(p<<1,pl,mid,ll,rr)); 30 if(rr>mid) 31 maxx=max(maxx,query(p<<1|1,mid+1,pr,ll,rr)); 32 return maxx; 33 } 34 int main() 35 { 36 LL n,t=0; 37 scanf("%d%lld",&m,&d); 38 for(int i=0;i<m;i++) 39 { 40 scanf("%*c%c%lld",&c,&n);//bzoj上能过,洛谷要改成字符数组 41 if(c=='A') 42 { 43 n=(n+t)%d; 44 ans++; 45 add(1,1,m,ans,n); 46 } 47 else 48 { 49 t=query(1,1,m,ans-n+1,ans); 50 printf("%lld\n",t); 51 } 52 } 53 return 0; 54 }
二、ST表
ST表是一种用于解决RMQ(查询区间最值)的算法,运用了dp思想,查询复杂度仅为O(1),本代码比线段树快了0.8秒左右,代码也更为简洁
简单介绍一下原理:
用一个数组st[i][j]来存储区间[i,i+(1<<j)-1]的最值,而该区间最值就等于[i,i+(1<<(j-1))-1]和[1<<(j-1),i+(1<<j)-1]的最大值,存储区间长度为2^j,所以预处理复杂度为O(nlogn)
查询时只需要将需查询区间[x,y]分成两部分,令k=(int)log2(y-x+1),区间[x,y]的长度就在2^k~2^(k+1)范围内,所以我们就可以将区间分成长度为2^k的两部分,即[x,x+(1<<k)-1],[y-(1<<k)+1,y],两部分可能有重合,只要保证覆盖了区间[x,y]即可
因为每次修改复杂度与预处理相同,所以不支持在线修改,但因为本题只需要在最后添加元素,所以我们可以反向建ST表(参考了大佬的思路),即st[i,j]存储[i-(1<<j)+1,i]的最值,这样每次修改时前面元素的ST表不受影响,每次修改的复杂度仅为O(logn)
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 int m,ans; 5 char c[2]; 6 LL d,st[200005][21]; 7 void add(int u,LL k) 8 { 9 st[u][0]=k; 10 for(int j=1;u-(1<<j)>=0;j++) 11 st[u][j]=max(st[u][j-1],st[u-(1<<(j-1))][j-1]); 12 } 13 LL ask(int l,int r) 14 { 15 int k=(log(r-l+1))/log(2); 16 return max(st[r][k],st[l+(1<<k)-1][k]); 17 } 18 int main() 19 { 20 LL n,t=0; 21 scanf("%d%lld",&m,&d); 22 for(int i=0;i<m;i++) 23 { 24 scanf("%s%lld",c,&n); 25 if(c[0]=='A') 26 { 27 n=(n+t)%d; 28 ans++; 29 add(ans,n); 30 } 31 else 32 { 33 t=ask(ans-n+1,ans); 34 printf("%lld\n",t); 35 } 36 } 37 return 0; 38 }
三、单调队列+并查集
本题只要求在数列末尾加数,所以很容易联想到单调队列,本代码比ST表又快了0.3秒左右
具体思路:
令队列中小于等于新元素的数全部出队,再另新元素入队,这样可以得到一个递减队列,队列中的每个数就代表数列中从该元素开始到末尾的最大值
假如要查询第x个元素到末尾的最大值,那么我们只需要从第x个元素开始,找第一个在单调队列里出现的数,该数即为该区间最大值
但如果要从x开始一个个遍历的话复杂度过高,这里就可以用并查集,将第i个元素与它后面第一个比它大的元素放到一个集合里,这样查询x时就可以直接找到后面第一个比它大的元素,并一直往后找,直到找不到更大的数为止
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define MAXN 200005 4 int m,d,ans,s;//ans为数列元素个数,s为队列元素个数 5 int a[MAXN];//原始数列 6 int q[MAXN];//单调队列,存储下标 7 int pre[MAXN];//并查集,存储数列中i之后第一个比a[i]大的数的下标 8 char c[2]; 9 int ufind(int x) 10 { 11 while(x!=pre[x]) 12 x=pre[x]; 13 return x; 14 } 15 int main() 16 { 17 int n,t=0; 18 scanf("%d%d",&m,&d); 19 for(int i=0;i<m;i++) 20 { 21 scanf("%s%d",c,&n); 22 if(c[0]=='A') 23 { 24 a[++ans]=(n+t)%d; 25 pre[ans]=ans;//每个数的上级初始化为自己 26 while(s>0&&a[q[s]]<a[ans]) 27 { 28 pre[q[s]]=ans;
29 s--; 30 }//将队列中小于新元素的上级改为新元素,并出队 31 q[++s]=ans;//新元素入队 32 } 33 else 34 { 35 t=a[ufind(ans-n+1)]; 36 printf("%d\n",t); 37 } 38 } 39 return 0; 40 }
四、树状数组
树状数组可用于单点修改,区间查询,本题符合该特点
需要注意的是,查询区间是从L到末尾,所以要反向建树状数组,即更新时从后往前更新,查询时从前往后查询
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int m,ans,d; 4 char c[2]; 5 int s[200005];//树状数组 6 int lowbit(int x) 7 { 8 return x&(-x); 9 } 10 void update(int x,int k) 11 { 12 for(int i=x;i>0;i-=lowbit(i)) 13 s[i]=max(s[i],k); 14 } 15 int query(int x) 16 { 17 int maxx=-1e9; 18 for(int i=x;i<=ans;i+=lowbit(i)) 19 maxx=max(maxx,s[i]); 20 return maxx; 21 } 22 int main() 23 { 24 int n,t=0; 25 scanf("%d%d",&m,&d); 26 for(int i=0;i<m;i++) 27 { 28 scanf("%s%d",c,&n); 29 if(c[0]=='A') 30 { 31 n=(n+t)%d; 32 ans++; 33 update(ans,n); 34 } 35 else 36 { 37 t=query(ans-n+1); 38 printf("%d\n",t); 39 } 40 } 41 return 0; 42 }
标签:maxnumber,洛谷,数列,最大数,int,查询,队列,ans,st 来源: https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10989177.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。