题目链接:https://nanti.jisuanke.com/t/38228
题意:给定数组,选择一个区间,使区间的最小值 乘 区间和 最大。
题解:
单调栈+ST表
因为存在负数,所以无法直接使用单调栈。可以先使用单调栈找出以当前数为最小值的左右区间。再使用ST表用前缀和和后缀和求出最值,之后再判断取最大值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn = 5e5 + 10;
ll a[maxn];
ll b[maxn];
ll ans[maxn];
ll l[maxn], r[maxn];
ll po2[20], lg2[maxn], mn[20][maxn];//po2[i]表示2的i次方 lg2[i]表示log(i) mn[i][j]表示从j到j+2^i-1的最大值
void ST(ll a[], int n)//数组 长度
{
po2[0] = 1, lg2[0] = -1;
for (int i = 1; i < 20; i++)
po2[i] = po2[i - 1] * 2;
for (int i = 1; i < maxn; i++)
lg2[i] = lg2[i / 2] + 1;
for (int i = 1; i <= n; i++)//显然i到i+2^0-1就i一个位置,那么最大值等于自己本身的值
mn[0][i] = a[i];
for (int i = 1; i <= lg2[n]; i++)
for (int j = 1; j <= n; j++)
if (j + po2[i] - 1 <= n)
mn[i][j] = max(mn[i - 1][j], mn[i - 1][j + po2[i - 1]]);//状态继承
}
ll ask(ll l, ll r)//2^log(a)>a/2
{
ll t = lg2[r - l + 1];
return max(mn[t][l], mn[t][r - po2[t] + 1]);
}
void ST1(ll a[], int n)//数组 长度
{
po2[0] = 1, lg2[0] = -1;
for (int i = 1; i < 20; i++)
po2[i] = po2[i - 1] * 2;
for (int i = 1; i < maxn; i++)
lg2[i] = lg2[i / 2] + 1;
for (int i = 1; i <= n; i++)//显然i到i+2^0-1就i一个位置,那么最小值等于自己本身的值
mn[0][i] = a[i];
for (int i = 1; i <= lg2[n]; i++)
for (int j = 1; j <= n; j++)
if (j + po2[i] - 1 <= n)
mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + po2[i - 1]]);//状态继承
}
ll ask1(ll l, ll r)//2^log(a)>a/2
{
ll t = lg2[r - l + 1];
return min(mn[t][l], mn[t][r - po2[t] + 1]);
}
int main()
{
ll MAX = 0;
ll n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
stack<ll > s;
for (int i = 1; i <= n; i++)//向左遍历的第一个比它小的元素的位置(使用单调递增栈)
{
while (!s.empty() && a[s.top()] >= a[i]) s.pop();//加等号后,为小于等于的数
if (s.empty()) l[i] = 0;
else l[i] = s.top();
s.push(i);
}
while (!s.empty()) s.pop();
for (int i = n; i >= 1; i--)//向右遍历的第一个比它小的元素的位置(使用单调递增栈)
{
while (!s.empty() && a[s.top()] >= a[i]) s.pop();//加等号后,为小于等于的数
if (s.empty()) r[i] = n + 1;
else r[i] = s.top();
s.push(i);
}
while (!s.empty()) s.pop();
memset(b, 0, sizeof(b));
for (int i = 1; i <= n; i++) b[i] = b[i - 1] + a[i];
ST(b, n);
for (int i = 1; i <= n; i++)
{
if (a[i] > 0)
{
ans[i] += ask(i,r[i]-1)-b[i];
}
}
memset(b, 0, sizeof(b));
for (int i = n; i >= 1; i--) b[i] = b[i + 1] + a[i];
ST(b, n);
for (int i = 1; i <= n; i++)
{
if (a[i] > 0)
{
ans[i] += ask(l[i]+1, i) - b[i];
ans[i] += a[i];
}
}
memset(b, 0, sizeof(b));
for (int i = 1; i <= n; i++) b[i] = b[i - 1] + a[i];
ST1(b, n);
for (int i = 1; i <= n; i++)
{
if (a[i] < 0)
{
ans[i] += ask1(i, r[i] - 1)-b[i];
}
}
memset(b, 0, sizeof(b));
for (int i = n; i >= 1; i--) b[i] = b[i + 1] + a[i];
ST1(b, n);
for (int i = 1; i <= n; i++)
{
if (a[i] < 0)
{
ans[i] += ask1(l[i] + 1, i)-b[i];
ans[i] += a[i];
}
}
for (int i = 1; i <= n; i++)
{
MAX = max(MAX, ans[i] * a[i]);
}
cout << MAX << endl;
return 0;
}