本文正在补充书写中
单调栈基础
例题1-求最大矩形面积
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
while (cin >> n) {
if (n == 0) break;
vector<int> h(n + 1), w(n + 1), stk;
h[n] = 0; // 多加一个高度为0的矩形,防止扫描完成后有剩余矩形
long long ans = 0;
for (int i = 0; i <= n; ++i) {
if (i != n) {
cin >> h[i];
}
int width = 0;
while (stk.size() && h[i] <= h[stk.back()]) {
width += w[stk.back()];
ans = max(ans, 1ll * width * h[stk.back()]);
stk.pop_back();
}
w[i] = width + 1;
stk.push_back(i);
}
cout << ans << "\n";
}
return 0;
}
例题2-区区区间间间
原题的题意可以理解为求所有子区间的最大值减去最小值的和。
即所有子区间的最大值减去最小值。
我们考虑用单调栈求解。
维护两个数组 l[i] ,r[i]。表示当前元素作为最大值所能到达的左边和右边的下标是多少(当前元素作为最值),用单调栈维护。
先正着维护左区间,再倒着维护右区间。
维护时的操作:当前元素大于前一个元素时,下标存上一个l[i]的下标,一直循环下去,直到当前数小于前面比较的数,左区间维护成功。右区间的维护是同样的道理。
维护最小值只需要把原来数组全部变成相反数,可以找到最小值。
求和:排列组合的思想,当前数作为最大值,左边到l[i] ,右边到r[i],左边的情况有i-l[i]种,右边情况有r[i]-i种,相乘即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[1<<17];
int l[1<<17],r[1<<17];
int n;
int solve(){
for(int i=1;i<=n;i++){
int j=i;
while(j>1 && a[j-1]<=a[i]) //当前数与前面的数进行比较
j=l[j-1];
l[i]=j;
}
for(int i=n;i;i--){
int j=i;
while(j<n && a[j+1]<a[i])
j=r[j+1];
r[i]=j;
}
int ans=0;
for(int i=1;i<=n;i++){
//cout<<l[i]<<" "<<r[i]<<endl;
ans += a[i]*(r[i]-l[i]);
ans += a[i]*(i-l[i])*(r[i]-i);
}
return ans;
}
signed main(){
int t;cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=solve();
for(int i=1;i<=n;i++) a[i]=-a[i];
cout<<ans+solve()<<endl;
}
return 0;
}
2022-11-11重做
求最大值所覆盖的区间使用单调递减栈。
求最小值所覆盖的区间使用单调递增栈。
每个值都有一个l[i],r[i]
数组,代表当前值作为最大(最小值)向左和向右最远可扩展的下标。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector<int>;
using pii = pair<int, int>;
void solve()
{
int n;
cin >> n;
vi a(n + 1);
for(int i = 1; i <= n; i++)
cin >> a[i];
ll ans = 0;
// mx
vi stk;
vi l(n + 1), r(n + 1, n);
for(int i = 1; i <= n; i++)
{
while(!stk.empty() && a[i] >= a[stk.back()])
{
r[stk.back()] = i - 1;
stk.pop_back();
}
l[i] = stk.empty() ? 1 : stk.back() + 1;
stk.push_back(i);
}
for(int i = 1; i <= n; i++)
ans += 1ll * a[i] * (i - l[i] + 1) * (r[i] - i + 1);
//mn
stk.clear();
vi L(n + 1), R(n + 1, n);
for(int i = 1; i <= n; i++)
{
while(!stk.empty() && a[i] <= a[stk.back()])
{
R[stk.back()] = i - 1;
stk.pop_back();
}
L[i] = stk.empty() ? 1 : stk.back() + 1;
stk.push_back(i);
}
for(int i = 1; i <= n; i++)
ans -= 1ll * a[i] * (i - L[i] + 1) * (R[i] - i + 1);
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
t = 1;
cin >> t;
while(t--)
solve();
return 0;
}