https://nanti.jisuanke.com/t/38228
给你一个序列,让你求出一个连续子序列,序列最小值 乘 序列和最大, n5e5, a[i] : [-1e5, 1e5];
只有正数就直接单调栈了,不会看这https://blog.youkuaiyun.com/swunHJ/article/details/89441597
考虑存在负数的情况:
如果a[i]是正的,那就在[l[i] - 1, i-1]这段找个最小的前缀和,在[i, r[i]]这段找个最大的前缀和,相减乘一下维护答案就好了
如果a[i]是负的,那就在[l[i] - 1, i -1]这段找个最大的前缀和,在[i, r[i]]这段找个最小的前缀和,相减乘一下维护答案就好了
没了
转换成了单调栈+RMQ问题,显然可以用线段树,但是直接st做更好写一点
int a[maxn], l[maxn], r[maxn];
ll sum[maxn], stmx[maxn][23], stmn[maxn][23];
int n;
stack<int> st;
void init(){
rep(i, 0, n) stmx[i][0] = stmn[i][0] = sum[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]);
stmn[i][j] = min(stmn[i][j-1], stmn[i+(1<<(j-1))][j-1]);
}
}
rep(i, 1, n){
while(!st.empty() && a[i] <= a[st.top()]) st.pop();
if(st.empty()) l[i] = 1;
else l[i] = st.top() + 1;
st.push(i);
}
while(!st.empty()) st.pop();
Rep(i, n, 1){
while(!st.empty() && a[i] <= a[st.top()]) st.pop();
if(st.empty()) r[i] = n;
else r[i] = st.top() - 1;
st.push(i);
}
}
ll askmx(int l,int r){
int k=log2(r-l+1);
return max(stmx[l][k],stmx[r-(1<<k)+1][k]);
}
ll askmn(int l,int r){
int k=log2(r-l+1);
return min(stmn[l][k],stmn[r-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
rep(i, 1, n) scanf("%d", a + i), sum[i] = sum[i-1] + a[i];
init();
ll ans = -INF;
rep(i, 1, n){
if(a[i] < 0){
ll l2 = askmx(l[i] - 1, i - 1), r2 = askmn(i, r[i]);
ans = max(ans, 1ll * (r2 - l2) * a[i]);
} else if(a[i] > 0){
ll l2 = askmn(l[i] - 1, i - 1), r2 = askmx(i, r[i]);
ans = max(ans, 1ll * (r2 - l2) * a[i]);
} else ans = max(ans, 0ll);
}
printf("%lld\n", ans);
return 0;
}