【题解】P1198 [JSOI2008]最大数

本文介绍使用单调队列解决动态查找倒数L位最大值的问题。通过建立单调递减队列并维护一个结构体来记录每个元素的有效区间长度,实现了高效查询。文章附带详细代码示例。
部署运行你感兴趣的模型镜像

单调队列一步到位)

思路

首先一看!有个查询操作,还有个添加操作,要找倒数L位的最大值。再一看操作的个数M <= 2e5,那么每次都查询都对后面L位找最大显然就不现实了。(优先队列破产)
虽然觉得不现实,但是还是用了(因为能混点分嘛)。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int m, d;

int main()
{
	vector<int> v;
	cin >> m >> d;
	int t = 0;
	for(int i = 0; i < m; i++)
	{
		string x; int k;
		cin >> x >> k;
		if(x[0] == 'A')
		{
			//cout << "t = " << t << endl;
			v.push_back((k + t) % d);
		}
		if(x[0] == 'Q')
		{
			priority_queue <int> q;
			int j = 0;
			while(k--)
			{
				j++;
				q.push(v[v.size() - j]);
			}
			cout << q.top() << endl;
			t = q.top();
		}
		
	}
	return 0;
}
然后就非常光荣的斩获了

TLE 30分

接着就洗心革面换上今天学的单调队列了。
我们用例子来讲会比较好理解。
1. 假设一开始进行了五步A操作加入了五个数
 	5 4 2 3 6 5
2. 然后我们一个个输入的时候就进行单调递减队列的建立。
5
5 > 4 -> 剩余:5 4
4 > 2 -> 剩余:5 4 2
2 < 3 -> 将2移除队列,剩余:5 4 3
3,4,5 < 6 将3 4 5都移除队列,剩余:6
6 > 5 -> 剩余:6 5
现在我们就完成了单调队列的创建,现在 6 是倒数第二位(L >= 2)以后的最大值,5 是倒数第一位(L = 1)的最大值,但是我们怎么让L >= 2的时候就能查询到 6 这个数?
这个也好解决!用一个结构体就完事啦!
typedef struct 
{
	//num 维护L区间,data存数字
	int num, data;	
}Node;
我们初始化的时候就每个数字的num设置为1,当我们在某个数要出列的时候,我们就在新入队的那个数字的 num(new) += 旧的数字的num,此时它就是从它开始往后面共num个数字的最大值。
我们要找倒数第L个数的最大值,那么我们就从后往前的每个数字的num值加起来,直到那个值大于或者等于L的时候我们就找到了倒数第L数间的最大值。
然后我们就做完这道题啦!看不懂?没事!我们再来模拟模拟刚才的例子就懂了!
 	5 4 2 3 6 5
5(num = 1)
5 > 4 -> 剩余:5(1) 4(1)
4 > 2 -> 剩余:5(1) 4(1) 2(1)
2 < 3 -> 将2移除队列,3(1 + 1) = 3(2)剩余:5(1) 4(1) 3(2)
3,4,5 < 6 将3 4 5都移除队列,6(1 + 1 + 1 + 2) = 6(5)剩余:6(1)
6 > 5 -> 剩余:6(5) 5(1)
Q查询 L = 2 -> 目前NUM = 0, 第一步加上数字 5 的num, NUM = 1 < L,比L还小,所以我们加上第二步的num,NUM = 6 > L,所以我们这时候就可以输出 6 ,它就是L = 2的最大值。
 	5 4 2 3 6 5
              ↑L = 2
Q查询 L = 6:
 	5 4 2 3 6 5
    ↑L = 6
这个时候的最大值也是 6 。(L >= 2 最大值均为 6 )

那我们就来看全部代码吧!

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e6 + 50;
int m, d;

typedef struct 
{
	//num 维护L区间,data存数字
	int num, data;	
}Node;

int main()
{
	//建立一个结构体型的容器 
	vector<Node> v;
	cin >> m >> d;
	//根据题意t一开始为0
	int t = 0;
	for(int i = 0; i < m; i++)
	{
		Node k; char x;
		cin >> x >> k.data;
		//往背后加数字的操作add 
		if(x == 'A')
		{
			//根据题意,我们要加的数字要加上t并对d取模 
			k.data = (k.data + t) % d;
			//初始化每个数字的num为1 
			k.num = 1;
			//构建单调队列,当队列有元素而且队列的最后一个元素小于要加入的那个元素时
			//将最后一个元素出队,新加入的元素的num要加上出队元素的num 
			//5 4 2 3 6 5
			//目前:5(1) 4(1) 2(1), 加入 3 ,2 < 3 -> 将2移除队列,剩余:5(1) 4(1) 3(2)
			while(v.size() && v.back().data < k.data)
			{
				k.num += v.back().num;
				v.pop_back();	
			}
			//将这个数的data和数的num(即整个结构体)入队
			v.push_back(k);
		}
		//查询操作Q 
		else if(x == 'Q')
		{
			//目前的num数量 nownum,一开始为0。
			int nownum = 0, j = 0; 
			//此时的k.data就是L,如果L大于 nownum就从后往前加上每个数的num直到L <= nownum
			//6(5) 5(1)
			// 查询 L = 1, nownum = 0,nownum += 1,nownum  = 1
			// nownum  == L, 输出此时的数字即为 5。 
			while(k.data > nownum)
			{
				j++;
			0	nownum += v[v.size() - j].num;
			}
			cout << v[v.size() - j].data << endl;
			t = v[v.size() - j].data;
		}
		
	}
	return 0;
}

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值