Codeforces Round #622 C2 - Skyscrapers(单调栈)

题意:

给一个整数 n n n以及一个数组 m m m。 构建一个数组 a a a满足: 1 ≤ a i ≤ m i 1≤a_i≤m_i 1aimi,且不存在这样的情况 j < i < k , a j > a i < a k j<i<k,a_j>a_i<a_k j<i<kaj>ai<ak

求数组的和最大的方案。

思路:

我们知道要满足数组的条件,数组必须是一个单峰数组。即我们只需要找到那个数组的峰值的下标即可。

用单调栈维护两个数组:

l [ i ] l[i] l[i]代表从 1 1 1 i i i 单调不减时最大的前缀和。

r [ i ] r[i] r[i]代表从 i i i n n n 单调不升时最大的后缀和。

那么我们找到 l [ i ] + r [ i ] − h [ i ] l[i] + r[i] - h[i] l[i]+r[i]h[i]的最大值下标就是目标数组的峰值下标,然后输出答案即可。

视频讲解可以参考:WNJXYK卿学姐

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 5e5+10;
ll n, l[maxn], r[maxn], h[maxn];
pair<int, int> s[maxn];
void solve(ll ans[]){
   
    int top = 0;
    ll cur = 0;
    s[top] = make_pair(0, 0);

    for (int i = 1; i <= n; i++){
   
        while(top && s[top].first > h[i]){
   
            cur -= (1ll * s[top].first * (s[top].second - s[top - 1].second));
            top--;
        }
        cur += (1ll * h[i] * (i - s[top].second));
        s[++top] = make_pair(h[i], i);
        ans[i] = cur;
    }
}
int main(){
   
    ios_base::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; i++){
   
        cin >> h[i];
    }
    solve(l);
    reverse(h + 1, h + 1 + n);
    solve(r);
    reverse(h + 1, h + 1 + n);
    reverse(r + 1, r + 1 + n);
    
    ll ans = r[1], pos = 1;
    for (int i = 1; i <= n; i++){
   
        ll t = l[i] + r[i] - h[i];
        if(ans < t){
   
            ans = t;
            pos = i;
        }
    }
    
    ll res[n + 1]; res[pos] = h[pos];
    for (int i = pos - 1; i >= 1; i--){
   
        res[i] = min(res[i + 1], h[i]);
    }
    for (int i = pos + 1; i <= n; i++){
   
        res[i] = min(res[i - 1], h[i]);
    }

    for (int i = 1; i <= n; i++){
   
        cout << res[i] << " ";
    }
    cout << endl;
    return 0;
}