一开始的思路只是单调栈+前缀和就好了,后来发现当最小值为负数的时候并不能找到最优解。
比如-3 7 -1 -2 -1
这种情况
正确的想法是对前缀和建立st表,还是单调队列求最近的小于x的编号。
当最小值<0的时候,应该求【i, R[i]】范围内的前缀和的最小 减去 【L[i], i-1】范围内前缀和最大。
反之求【i, R[i]】范围内的前缀和的最大 减去 【L[i], i-1】范围内前缀和最小。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
int Case = 1;
int n, m;
ll cc[maxn];
ll L[maxn], R[maxn];
ll stmi[maxn][30], stmx[maxn][30], pre[maxn];
void initst() {
for(int i = 1; i <= n; i++) pre[i] = pre[i-1] + cc[i];
for(int i = 0; i <= n; i++) stmi[i][0] = stmx[i][0] = pre[i];
for(int j = 1; (1<<j) <= n+1; j++) {
for(int i = 0; i+(1<<j)-1 <= n; i++) {
stmx[i][j] = max(stmx[i][j-1], stmx[i+(1<<(j-1))][j-1]);
stmi[i][j] = min(stmi[i][j-1], stmi[i+(1<<(j-1))][j-1]);
}
}
}
ll askmx(int l, int r) {
int k = log2(r-l+1);
return max(stmx[l][k], stmx[r-(1<<k)+1][k]);
}
ll askmi(int l, int r) {
int k = log2(r-l+1);
return min(stmi[l][k], stmi[r-(1<<k)+1][k]);
}
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", &cc[i]);
}
initst();
stack<int>st;
for(int i = 1; i <= n; i++) {
while(st.size() && cc[st.top()] >= cc[i]) {
st.pop();
}
if(st.empty()) L[i] = 1;
else L[i] = st.top() + 1;
st.push(i);
}
while(!st.empty()) st.pop();
for(int i = n; i >= 1; i--) {
while(st.size() && cc[st.top()] >= cc[i]) {
st.pop();
}
if(st.empty()) R[i] = n;
else R[i] = st.top()-1;
st.push(i);
}
ll res = -1e18;
for(int i = 1; i <= n; i++) {
if(cc[i] < 0) {
ll r = askmi(i, R[i]);
ll l = askmx(L[i]-1, i-1);
res = max(res, (r-l)*cc[i]);
}
else {
ll r = askmx(i, R[i]);
ll l = askmi(L[i]-1, i-1);
res = max(res, (r-l)*cc[i]);
}
}
printf("%lld\n", res);
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
//freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
while(Case--) {
solve();
}
return 0;
}