当前位置: 首页 > news >正文

学习笔记——单调数据结构

单调栈

题目

定义:一个下标 $i$ 是序列 $a$ 的后缀最大值下标当且仅当对于所有的 $i < j \le |a| $ ,都有 $a_i>a_j$。其中 $|a|$ 表示序列 $a$ 的长度。给出整数 $n$ 和一个长为 $n$ 的序列 $a$,对于每个 $1 \le i \le n$ ,输出 $a_1 \sim a_i$ 所有后缀最大值下标的异或和。

思路

考虑用一个栈来维护当前 $a_1 \sim a_i$ 所有的后缀最大值下标,容易发现栈中的数单调递增(指栈顶到栈底的下标对应的值单调递增)。当遍历到 $i$ 时,如果栈顶存放的下标 $j$ 满足 $a_j>a_i$,则因为栈是单调递增的,栈中的其他下标对应的值均大于 $a_i$,可以正确维护。如果不满足条件则需要不断弹出栈顶直到满足条件。并将 $i$ 入栈,栈中的下标即为后缀最大值下标

Code:

vector<int> stk;
int ans=0;
for(int i=1;i<=n;i++){while(!stk.empty()&&a[stk.back()]<=a[i]){ans^=stk.back();stk.pop_back();}stk.push_back(i);cout<<ans<<" ";
}

复杂度

共有 $n$ 次循环且共执行了 $n$ 次入栈操作,时间复杂度 $O(n)$。

单调栈需要额外空间,空间复杂度 $O(n)$ 。

单调队列

题目

将单调栈的问题稍加变化:给出整数 $n, k$ 和一个长为 $n$ 的序列 $a$,对于每个长度为 $k$ 的区间,输出该区间内所有后缀最大值下标的数量。

思路

这个问题对比上个问题来说多了一个删除过期元素的功能。可以用 STL 中的 deque 双端队列来维护。每次循环开始时先从队首(front)取出过期元素。再按照单调栈的方法维护

单调队列还可以求区间最小/最大值,将新的下标加入队列中后队首的下标就是最值所在的下标

Code

deque<int> q;
int ans=0;
for(int i=1;i<=n;i++){while(!q.empty()&&q.front()<=i-k){ans--;q.pop_front();}while(!q.empty()&&a[q.back()]<=a[i]){ans--;q.pop_back();}q.push_back(i);ans++;if(i>=k)cout<<ans<<" ";
}

复杂度

时间复杂度 $O(n)$

空间复杂度 $O(n)$

例题

P6510 奶牛排队

题意

给定一个长度为 $n$ 的序列 $a$,求出满足区间内 $a_l$ 最小且 $a_r$ 最大的区间 $[l,r]$ 的最大长度。( $2 \le N \le 10^5$ )

思路

考虑暴力,枚举右端点 $r$ ,对每个 $r$ 求出符合条件的 $l$ 。时间复杂度 $O(n^2)$ ,对 $10^5$ 的数据显然会超时。

因为 $a_l < a_i$ ( $l < i \le r$ ),所以 $l$ 一定是 $1 \sim r$ 的后缀最小值。又因为 $l \sim r$ 中 $a_r$ 最大,即这段区间不能有任何一个数比 $a_r$ 大。可以二分求出 $l$ 的值。

用单调栈 $s1$ 和 $s2$ 分别维护后缀最大值和最小值,每次循环时在 $s2$ 上二分找出第一个大于 $s1.back()$ (即栈顶)的下标,更新答案。时间复杂度 $O(n \log n)$ 。

Code

#include<bits/stdc++.h>
using namespace std;
int main(){int n; cin>>n;vector<int> a(n+1);for(int i=1;i<=n;i++)cin>>a[i];vector<int> s1,s2;int ans=0;for(int r=1;r<=n;r++){while(s1.size()&&a[s1.back()]<=a[r])s1.pop_back();while(s2.size()&&a[s2.back()]>=a[r])s2.pop_back();auto l=upper_bound(s2.begin(),s2.end(),s1.back());if(l!=s2.end())ans=max(ans,r-*l+1);s1.push_back(r);s2.push_back(r);}cout<<ans;return 0;
}

P13889 子矩阵

题意

有一个 $n \times m$ 个整数组成的矩阵,求所有大小为 $a \times b$ 的子矩阵中最大值与最小值的乘积的和。

思路

考虑暴力做法,先找出每个子矩形,再计算最小值和最大值,统计答案,时间复杂度 $O(nmab)$ ,显然不能通过本题。

我们可以使用单调队列进一步优化,对于输入的 $n \times m$ 的矩阵,我们可以把每一行看作一个单独的数列,对其求出它的每个长度为 $b$ 的子段的区间最大值,易得出这样的最大值有 $n \times (m-b+1)$ 个,将每行的最大值构成新矩阵 $mx1$ 的一行,则 $mx1$ 的大小是 $n \times (m-b+1)$ 。

对新矩阵,再将它的每一列看作一个单独的数列,求出这一列上每个长度为 $a$ 的子段的区间最大值,存到另一个矩阵 $mx2$ 中, $mx2$ 的大小就是 $(n-a+1) \times (m-b+1)$ 。最小值同理。

经过这一步,我们可以得出两个存储原矩阵的子矩阵最值的矩阵,最后统计答案即可。

Code

#include<bits/stdc++.h>
using namespace std;int main(){int n,m,a,b; cin>>n>>m>>a>>b;vector<vector<int>> c(n+1,vector<int>(m+1));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>c[i][j];}}vector<vector<int>> mx1(n+1,vector<int>(m-b+2)),mn1(n+1,vector<int>(m-b+2));vector<vector<int>> mx2(n-a+2,vector<int>(m-b+2)),mn2(n-a+2,vector<int>(m-b+2));for(int i=1;i<=n;i++){deque<int> q;for(int j=1;j<=m;j++){while(q.size()&&q.front()<j-b+1)q.pop_front();while(q.size()&&c[i][q.back()]<=c[i][j])q.pop_back();q.push_back(j);if(j>=b)mx1[i][j-b+1]=c[i][q.front()];}}for(int j=1;j<=m-b+1;j++){deque<int> q;for(int i=1;i<=n;i++){while(q.size()&&q.front()<i-a+1)q.pop_front();while(q.size()&&mx1[q.back()][j]<=mx1[i][j])q.pop_back();q.push_back(i);if(i>=a)mx2[i-a+1][j]=mx1[q.front()][j];}}for(int i=1;i<=n;i++){deque<int> q;for(int j=1;j<=m;j++){while(q.size()&&q.front()<j-b+1)q.pop_front();while(q.size()&&c[i][q.back()]>=c[i][j])q.pop_back();q.push_back(j);if(j>=b)mn1[i][j-b+1]=c[i][q.front()];}}for(int j=1;j<=m-b+1;j++){deque<int> q;for(int i=1;i<=n;i++){while(q.size()&&q.front()<i-a+1)q.pop_front();while(q.size()&&mn1[q.back()][j]>=mn1[i][j])q.pop_back();q.push_back(i);if(i>=a)mn2[i-a+1][j]=mn1[q.front()][j];}}long long ans=0;long long mod=998244353;for(int i=1;i<=n-a+1;i++){for(int j=1;j<=m-b+1;j++){ans=(ans+(mx2[i][j]*1LL*mn2[i][j]%mod))%mod;}}cout<<ans;return 0;
}
http://www.jsqmd.com/news/384811/

相关文章:

  • 大润发卡用不上?线上回收 92 折起,几分钟到账 - 可可收
  • 2026年昆明管道疏通哪家强?昆明管道疏通服务评测与推荐,解决效率与安全痛点 - 十大品牌推荐
  • 管道疏通哪家强?2026年荆州管道疏通服务推荐与权威评价 - 十大品牌推荐
  • 2026年柳州管道疏通推荐:基于多场景实测评价,针对管道老化与效率低下痛点精准指南 - 十大品牌推荐
  • 管道疏通服务哪家强?2026年荆门管道疏通推荐与排名,解决技术性与安全隐忧核心痛点 - 十大品牌推荐
  • 推进科研和工程,编程跻身顶级人类竞赛榜:谷歌Gemini 3 Deep Think重大升级
  • GESP认证C++编程真题解析 | 202509 五级
  • 这份榜单够用!8个AI论文写作软件测评:专科生毕业论文+开题报告高效工具推荐
  • 管道堵塞与清淤难题如何解决?2026年荆州管道疏通服务推荐与评价 - 十大品牌推荐
  • 实测对比后 9个降AIGC工具:继续教育降AI率全维度测评
  • 闲置苏宁卡别再压箱底!这样处理,轻松盘活不用愁 - 可可收
  • 2.15假期记录
  • 老年人氨糖求推荐 特元素氨糖软骨素成2026年高端氨糖品类合规选购新参照 - 资讯焦点
  • 2026汽车保养攻略:厂家推荐品牌详细评测,汽车保养/大车轮胎/客车轮胎/轿车轮胎/汽车维修,汽车保养厂家排行榜 - 品牌推荐师
  • 开题卡住了?AI论文软件 千笔AI VS 文途AI,专科生专属神器!
  • 2026年荆门管道疏通哪家强?市政与家庭场景全面评测排名与推荐 - 十大品牌推荐
  • 专科生必看!实力封神的降AI率平台 —— 千笔·降AIGC助手
  • 综述不会写?AI论文平台 千笔ai写作 VS 笔捷Ai,本科生专属神器!
  • 本科生必看!千笔·专业降AI率智能体,备受喜爱的降AIGC平台
  • Sigma-delta DAC simulink模型 128/256 oversampling...
  • 论文写不动?千笔·专业论文写作工具,最受喜爱的AI论文工具
  • 【信息科学与工程学】【产品体系】第十二篇 制造业生产加工06
  • GESP认证C++编程真题解析 | 202509 四级
  • 混合动力汽车P2架构cruise-simulink仿真模型,P2架构整车能量管理cruise仿真模型
  • 别让支付宝立减金白白过期!这样处理,省心又实用 - 可可收
  • 道路表面多类型缺陷的图像识别数据集分享(适用于目标检测任务)
  • 震旦大厦广告代理:2026年户外LED大屏广告投放的优选伙伴,广播电台广告,户外led大屏广告代理公司推荐 - 品牌推荐师
  • 杉德斯玛特卡如何回收更划算?专家教你避开回收陷阱! - 团团收购物卡回收
  • 从制造到服务:盘点工厂预制化管道领域的实力厂家,高压管件/三通管件/防腐管道/压力容器,工厂预制化管道品牌有哪些 - 品牌推荐师
  • 导师严选! 降AIGC软件 千笔·降AIGC助手 VS 云笔AI,本科生专属神器!