首页 > 其他分享 >单调栈

单调栈

时间:2025-01-20 20:21:03浏览次数:1  
标签:int top st 后缀 include 单调 define

模板

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+6;
#define pii pair<int,int>
#define fi first
#define se second
int n,a[N],top,b[N];
pii st[N];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=n;i;i--){	
		while(top&&a[i]>=st[top].fi){
			top--;
		}
		if(!top)b[i]=0;
		else{
			b[i]=st[top].se;
		}
		st[++top].fi=a[i],st[top].se=i;
	}
	for(int i=1;i<=n;i++)cout<<b[i]<<" ";
	return 0;
}

解析

求每个数后第一个大于他的数

从后往前,用栈去做一个东西:每次加一个数进来,比较他跟栈顶,如果他比栈顶小则直接放入,否则弹出栈顶直到栈顶比这个数大,放入这个数

一句话概括就是维护一个单调递减的栈

为什么这么做?

要找第一个比他大的,那么如果一个小数在一个大数后面,那么他一定不会对前面的数的答案有贡献(全被前面那个大数挡上了)

但是如果前面的数比后面的小,那么后面的数还是有用的(碰到一个介于两者之间的数)

这就是所谓的 如果一个人比你小还比你巨,那么你就没用了

其实碰见云落哥哥的我早就没用了

优化

应用

云落姐姐说咕了

例题

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

读不懂题

幸好有天天 AK 的云落大巨

后缀最大值

我们会发现

如果一个数在你后面还比你大

那么你无论如何都成为不了后缀最大值

那么维护一个单调递减的单调栈即可

#include <bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define pii pair<int,int>
#define fi first
#define se second
const int N=1e6+6;
int n,a[N],top,b[N],ans;
pii st[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		while(top&&a[i]>=st[top].fi){
			ans^=st[top].se;
			top--;
		}
		st[++top].se=i,st[top].fi=a[i]; 
		ans^=i;
		cout<<ans<<endl;
	}
	return 0;
}

奶牛排队

突然想到,上面两道题根本不用 pair,直接记下标,取值的时候套个 a 即可

呜呜呜,我好菜

云落哥哥救我~

嘻嘻

圆规正转

我们枚举 \(B\)

那么 \(A\) 一定是 \(1-B\) 的后缀最小值的位置

并且由于 \(B\) 要是区间最大值,也就是说,第二个后缀最大值的位置一定在 \(A\) 左面

那么我们就通过 第二个后缀最大值的位置,二分出最靠右的 \(A\) 的位置

上面的后缀最大值和后缀最小值就用单调栈求

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,ans,topx,topn;
int a[N],stx[N],stn[N];
int main() {
  cin>>n;
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int i=1;i<=n;i++) {
    while(topn&&a[stn[topn]]>=a[i])topn--;
    while(topx&&a[stx[topx]]<a[i])topx--;
    int k=upper_bound(stn+1,stn+1+topn,stx[topx])-stn;
    if(k!=(topn+1)){
      ans=max(ans,i-stn[k]+1);
    }
    stn[++topn]=i;
    stx[++topx]=i;
  }
  cout<<ans;
  return 0;
}

DIFERENCIJA

这是一个经典操作:找到一个值为为最大值的最大区间长度/区间个数

那么在单调栈里这个数的前一个数就是他的左边界限,反过来做一下就能确定右端点

最小值一样

直接贴一个很清晰的题解代码

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<stack>

#define int long long
const int MN=3e5+5;

using namespace std;

int a[MN];
int Lmax[MN],Rmax[MN],Lmin[MN],Rmin[MN];
stack<int>s;
int n;

signed main(void){

    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];

    for(int i=1;i<=n;i++){
        while(!s.empty()&&a[s.top()]<=a[i])s.pop();
        if(s.empty())Lmax[i]=0;
        else Lmax[i]=s.top();
        s.push(i);
    }

    while(!s.empty())s.pop();
    for(int i=1;i<=n;i++){
        while(!s.empty()&&a[s.top()]>=a[i])s.pop();
        if(s.empty())Lmin[i]=0;
        else Lmin[i]=s.top();
        s.push(i);
    }

    while(!s.empty())s.pop();
    for(int i=n;i>=1;i--){
        while(!s.empty()&&a[s.top()]<a[i])s.pop();
        if(s.empty())Rmax[i]=n+1;
        else Rmax[i]=s.top();
        s.push(i);
    }

    while(!s.empty())s.pop();
    for(int i=n;i>=1;i--){
        while(!s.empty()&&a[s.top()]>a[i])s.pop();
        if(s.empty())Rmin[i]=n+1;
        else Rmin[i]=s.top();
        s.push(i);
    }

    int ans=0;
    for(int i=1;i<=n;i++){
        ans=ans+a[i]*(i-Lmax[i])*(Rmax[i]-i);
        ans=ans-a[i]*(i-Lmin[i])*(Rmin[i]-i);
    }

    cout<<ans<<endl;

    return 0;
}

パッと光って咲いた

花火を見ていた

标签:int,top,st,后缀,include,单调,define
From: https://www.cnblogs.com/sunxuhetai2009/p/18682463

相关文章

  • 决策单调性
    决策单调性四边形不等式定义若对于\(\foralli\lej\lek\lel\),有\(W_{i,k}+W_{j,l}\leW_{i,l}+W_{j,k}\),则称\(W\)满足四边形不等式。性质&判定对于\(\foralli\ltj\),有\(i\lti+1\lej\ltj+1\),于是\(W_{i,j}+W_{i+1,j+1}\leW_{i,j+1}+W_{i+1,j}\),这是显然......
  • 单调队列优化dp
    一本通题解T2:再也不想碰这道题了。。。写了一下午。我们先设状态\(dp[i][j]\)表示前i个刷匠,考虑了前\(j\)个木板后所获得的最大价值(\(j\)个木板可以有空余)。然后枚举前\(i\)个刷匠,枚举每一条木板。对于一条木板可以此刷匠根本不刷,或不刷当前木板,状转方程:\[dp[i][j]......
  • Java-数据结构-栈与队列(常考面试题与单调栈)
    在上一篇的学习中,我们学习了栈和队列的基本知识,以及它们对应都有哪些方法,在什么应用场景下如何使用,并且还对它们进行了模拟实现,而其实对于栈和队列的相关知识还远不止于此,而今天我们就对栈与队列进行复盘,认识更多使用它们的场景,夯实代码功底吧~一、常考面试题-思维以下习题在......
  • leetcode周赛432 T4(单调栈 + 单调队列)
    一道练习单调栈+单调队列的好题题目链接:problem对于求合法子数组数量的题目,可以先考虑传统的枚举右端点,二分左端点的套路。此题用这种方法恰好可行,因为对于一个序列,左端增加一个数不会让操作数更少。因此对于固定右端点,合法的左端点一定是一段区间。所以现在问题转化为:用双指......
  • 最大矩形(单调栈)
    题目链接:https://leetcode.cn/problems/maximal-rectangle/description/题意:给定一个只有0和1的矩阵,试求只包含1的长方形的最大面积思路:每行将矩阵压缩成一个高度数组,转化为求矩形最大面积classSolution{public:intmaximalRectangle(vector<vector<char>>&matrix)......
  • 子数组最小值(单调栈)
    题目链接:https://leetcode.cn/problems/sum-of-subarray-minimums/题意:给定一个数组,让你求子数组最小值的和,复杂度O(N)思路:单调栈(获得每个位置左边比它小且离它最近的数,右边比它小且离它最近的数,那么在这之间它本身就是区间最小值)classSolution{public:intsumSubarr......
  • 每日温度(单调栈)
    题目链接:https://leetcode.cn/problems/daily-temperatures/题意:给你一个每日气温数组,请你确定每个位置右边是否比自己大的元素,如果无,返回0。否则,返回两者下标之差思路:单调栈(这就好似给了数组中每个位置做波峰或波谷的机会)(ps:单调栈一定存的是下标i)classSolution{public......
  • 单调栈板子
    单调栈用于求解数组每个位置上左边/右边离自己最近的且严格小于/大于自己位置上的数的位置时间复杂度O(N)(每个元素下标进栈一次出栈一次)元素下标能表示的含义比元素本身要多(ps:注意数组长度,过大就要开到全局变量中,否则异常退出orz)方法(求每个位置上离自己最近且严格小于自己......
  • Codeforces 319B Psychos in a Line 题解 [ 绿 ] [ 单调栈 ] [ 动态规划 ] [ adhoc ]
    PsychosinaLine:很好的单调栈优化dp题!观察我们先观察,一个精神病人会一直杀到什么时候。显然,会杀到右边第一个比他大的精神病人那里,然后他就杀不动了。因此我们可以从右往左考虑,算出左边的精神病人杀掉这个精神病人后左边的人的答案是什么。假设左边的人目前已经刀了\(x\)......
  • 柱状图中最大的矩形(单调递增栈)
    给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1。求在该柱状图中,能够勾勒出来的矩形的最大面积。示例1:输入:heights=[2,1,5,6,2,3]输出:10解释:最大的矩形为图中红色区域,面积为10示例2:输入:heights=[2,4]输出:4代码思想:进栈......