单调栈 && 单调队列

单调栈

适用于求左边或者右边离它最近的(比它大或者小)的数

1>【模板】单调栈

题目描述

给出项数为 n 的整数数列 a1n

定义函数 f(i) 代表数列中第 i 个元素之后第一个大于 ai 的元素的下标,即 f(i)=mini<jn,aj>ai{j}。若不存在,则 f(i)=0

试求出 f(1n)

输入格式

第一行一个正整数 n

第二行 n 个正整数 a1n

输出格式

一行 n 个整数表示 f(1),f(2),,f(n) 的值。

样例

5
1 4 2 3 5
2 5 4 5 0

提示

对于 100% 的数据,1n3×1061ai109

思路

设当前数为a 2
a 2 之后第一个比a 2 大的数 <=>反转这串数,a 2 之前且离a 2 最近的比a 2 大的数
栈顶元素刚好满足这一要求,如果该栈是单调递减的
由于要输出下标,所以就将下标存入栈中

代码

#include <iostream>
#include <stack>
#include <vector>
using namespace std;
const int N = 3e6 + 10;
stack<int> a;
int b[N];
vector<int> c;
int main()
{
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = n; i > 0; i--) //起到反转的效果
{
while(!a.empty() && b[a.top()] <= b[i]) a.pop(); //不符合单调递减的条件就会一直出栈
c.push_back(a.empty() ? 0 : a.top()); //若栈为空,则没有元素比它大;若非空,则栈顶元素就是第一个大于它的
a.push(i);
}
for(int i = c.size() - 1; i >= 0; i--) cout << c[i] << ' ';
}

2>求数列所有后缀最大值的位置

题目描述

给定一个数列 a,初始为空。有 n 次操作,每次在 a 的末尾添加一个正整数 x

每次操作结束后,请你找到当前 a 所有的后缀最大值的下标(下标从 1 开始)。一个下标 i 是当前 a 的后缀最大值下标当且仅当:对于所有的 i<j|a|,都有 ai>aj,其中 |a| 表示当前 a 的元素个数。

为了避免输出过大,请你每次操作结束后都输出一个整数,表示当前数列所有后缀最大值的下标的按位异或和

输入格式

第一行是一个整数,表示操作次数 n
第二行有 n 个整数,依次表示 n 次操作所添加的整数 xi

输出格式

每次操作后请输出一行一个整数,表示当前数列所有后缀最大值下标的按位异或和。

样例

5
2 1 3 5 4
1
3
3
4
1

提示

对于全部的测试点,保证 1n1061xi<264

思路

后缀最大值 单调递减栈的栈底元素

代码

#include <iostream>
#include <stack>
using namespace std;
typedef unsigned long long llu;
const int N = 1e6 + 10;
stack<llu> a;
llu b[N], count;
int main()
{
llu n; scanf("%llu", &n);
for(llu i = 1; i <= n; i++)
{
scanf("%llu", &b[i]);
while(!a.empty() && b[a.top()] <= b[i]) count ^= a.top(), a.pop();
count ^= i, a.push(i);
printf("%llu\n", count);
}
}

单调队列

适用于求区间最值, 队列头就是最值

思考步骤

  • 判断是否需要弹出队尾(直至满足单调性)
  • 插入元素 <若在最后才插入元素,当队列为空的情况可能会随机输出一个数>
  • 判断队头是否要出队(区间长度超过特定值)
  • 只有至少构成了一个窗口,才能输出队头

代码

#include <iostream>
#include <deque>
using namespace std;
const int N = 1e6 + 10;
deque<int> a; //存数字
int b[N];
int main()
{
int n, k; cin >> n >> k;
for(int i = 0; i < n; i++) cin >> b[i];
//min
for(int i = 0; i < n; i++)
{
//判断队尾元素是否要出队
while(a.size() && a.back() > b[i]) a.pop_back();
//新元素入队
a.push_back(b[i]);
//判断队头元素是否要出队(队伍长度是否大于k)
if(i - k >= 0 && b[i - k] == a.front()) a.pop_front();
//构成了一个区间,则输出队头元素,队头就是最值
if(i + 1 >= k) cout << a.front() << ' ';
}
cout << endl;
a.clear(); //记得清空
//max
for(int i = 0; i < n; i++)
{
while(a.size() && a.back() < b[i]) a.pop_back();
a.push_back(b[i]);
if(i - k >= 0 && b[i - k] == a.front()) a.pop_front();
if(i + 1 >= k) cout << a.front() << ' ';
}
}
#include <iostream>
#include <deque>
using namespace std;
const int N = 1e6 + 10;
int b[N];
deque<int> a; //存下标
int main()
{
int n, k; cin >> n >> k;
for(int i = 0; i < n; i++) cin >> b[i];
//min
for(int i = 0; i < n; i++)
{
while(!a.empty() && b[a.back()] > b[i]) a.pop_back();
a.push_back(i);
if(i - k >= 0 && b[a.front()] == b[i - k]) a.pop_front();
if(i + 1 >= k) cout << b[a.front()] << ' ';
}
a.clear();
cout << endl;
//max
for(int i = 0; i < n; i++)
{
while(!a.empty() && b[a.back()] < b[i]) a.pop_back();
a.push_back(i);
if(i - k >= 0 && b[a.front()] == b[i - k]) a.pop_front();
if(i + 1 >= k) cout << b[a.front()] << ' ';
}
}
posted @   PeachyGalaxy  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示