首先思考一个问题:快速求出一个数组每一位比他小的左边最近的位置
贪心思想:如果a[i]<a[j] (i>j) 那么a[j]将会用不到
每一个元素入栈时,如果栈顶元素比它大,那么一直弹出栈顶,否则直接让这个数入栈。
不难发现,该算法时间复杂度为O(n)
练习:
7-4 单调栈
分数 10
全屏浏览题目
切换布局
作者 李曲
单位 浙江工业大学
米尔科发现了斯拉夫科在前一个任务中所做的事情,并决定处理与字母表完全相反的事情:数字序列。
让我们将序列的值定义为该序列中最大和最小数字之间的差值。例如,序列(3,1,7,2)的值是6,(42,42)的值是0。
求给定序列中连续元素的所有子序列的值之和。
输入格式:
第一行输入包含一个整数N(2≤N≤300000),表示序列的元素数。
接下来的N行包含序列的元素。每个元素都是不大于100000的正整数。
输出格式:
输出所要求的和。
输入样例1:
3
1
2
3
输出样例1:
4
输入样例2:
4
7
5
7
5
输出样例2:
12
输入样例3:
4
3
1
7
2
输出样例3:
31
题解:本题要求所有区间的最大值与最小值差 的和。
那么可以分成两部分:求所有区间的最大值和 减去 所有区间的最小值和。
利用单调栈:以求最大值举例: 开数组vv1 记录端点i左边所有区间的最大值和
固定一个右端点i 我们可以找到它左边第一个比它大的下标设为id
如何id不存在的话 那么i的值肯定是从i到左最大的 v1[i]=i*a[i]
如果id存在 v1[i]=v1[st.top()]+(i-st.top())*a[i]; 为两部分 一部分栈顶的值为最大值 一部分是a[i]
(栈顶存的是下标 下标从1开始)
代码如下:
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll ans; int main(){ int n; cin>>n; vector<ll>a(n+1),v1(n+1),v2(n+1); for(int i=1;i<=n;i++) cin>>a[i]; stack<int>st; for(int i=1;i<=n;i++){ while(!st.empty()&&a[i]>a[st.top()]) st.pop(); if(!st.empty()) v1[i]=v1[st.top()]+(i-st.top())*a[i]; else v1[i]=a[i]*i; ans+=v1[i]; st.push(i); } while(!st.empty())st.pop(); for(int i=1;i<=n;i++){ while(!st.empty()&&a[i]<=a[st.top()]) st.pop(); if(!st.empty()) v2[i]=v2[st.top()]+(i-st.top())*a[i]; else v2[i]=a[i]*i; ans-=v2[i]; st.push(i); } cout<<ans; }