BZOJ 1012 [JSOI2008]最大数maxnumber (单调栈)

1012: [JSOI2008]最大数maxnumber

Time Limit: 3 Sec   Memory Limit: 162 MB
Submit: 11124   Solved: 4868
[ 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

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

Sample Output

96
93
96


线段树可以做,RMQ的模板题,新学到一种处理区间问题的思想,单调栈也可以说是单调队列。维护一个栈,栈内元素入栈先后和元素的大小都是有序的,最后取值的时候只需要二分查找最大数的位置,最后输出即可。

由于后入栈的数如果比先入栈的数大的话,先入栈的数不会输出,所以如果后入栈的数大,就让它直接覆盖掉比它小的数中的最大数,栈中此位置储存当前值在原数组中的下标,因为每次询问都是在上一次L的基础上,所以此题可以用单调队列维护。

其实模拟栈的数组储存的就是原数组的下标,数组中代表原数组元素递减排序,这样可以表示当前最大值可以覆盖后面的较小值,二分查找就是在最后一个较大值前面的大数中,查找符合要求的数的下标,最后输出。

想了两天,自己想通了,可还是不怎么会表达,等以后积淀一下再做总结吧。

注意,cin和cout会RE,我交了差不多20次才A到,这就是玄学的奇妙之处吧。

代码实现:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))

using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=2e5+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll ans[maxn],Stack[maxn];              
//Stack单调栈,入栈元素入队先后与数的大小都有序 ,ans储存修改后的值 
int main()
{
	char x;
	int i,j,k,n,d,y,top,len,END;        //END储存后L个数中的最大数,top记录当前最大值在栈中的位置,len记录元素个数 
	while(scanf("%d %d",&n,&d))
	{
		mset(ans,0);
		mset(Stack,0);
		END=len=top=0;
		for(i=1;i<=n;i++)
		{
			getchar();
			scanf("%c %d",&x,&y);
			if(x=='A')
			{
				y=(y+END)%d;                             //最大值加上y后取模d 
				ans[++len]=y;                            //y入队列,下标自加 
				while(top&&ans[Stack[top]]<=y)           //栈不为空,倒序排序 
				top--;
				 
				Stack[++top]=len;                        //到当前栈中这个元素对应的原数组的下标 
			}
			else
			{
				int temp=lower_bound(Stack+1,Stack+top+1,len-y+1)-Stack;    //找到最大值下标的位置 
				END=ans[Stack[temp]];                                       //输出并记录后L个数的最大值 
				printf("%d\n",END);  
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值