题目链接
N个数字求一个区间使得
最大
思路
- 枚举区间的最小值为
,根据ST表二分找到它 最左
和最右
端点,这是保证区间
的最小值为
记录前缀和
记录后缀和
找到区间之后分两种情况
-
因为 所以我们在区间 找到一个最大的前缀和, 在区间 找到一个最大的后缀和这样就能保证值最大 -
和上一种情况相反
因为 所以我们在区间 找到一个最小的前缀和, 在区间 找到一个最小的后缀和这样就能保证值最大
Ac之路很坎坷,刚开始全用ST表维护最大值最小值,结果MLE(ST很快但是空间也很高),然后把求后缀的ST表用线段树写,还是MLE。前后缀都带用线段树写,两个线段树代码重复太高而且变量名也不好起,还没用C++写过类,学习下杰哥用类写的线段树。Orz。。。
AC
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int maxn = 5e5 + 5;
int a[maxn];
int dp[maxn][20];
LL pre[maxn], suf[maxn];
void ST(int n) {
for (int i = 1; i <= n; ++i) {
dp[i][0] = a[i];
}
for (int j = 1; j <= log2(n); ++j) {
for (int i = 1; i+(1<<j)-1 <= n; ++i) {
dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
}
}
LL RMQ(int l, int r) {
int len = r - l + 1;
int x = log2(len);
return min(dp[l][x], dp[r - (1<<x)+1][x]);
}
class Seg{
LL Max[maxn<<2], Min[maxn<<2];
public: void build(int rt, int l, int r, LL a[]) {
if (l == r) {
Max[rt] = a[l];
Min[rt] = a[l];
return;
}
build(lc, l, mid, a);
build(rc, mid+1, r, a);
Max[rt] = max(Max[lc], Max[rc]);
Min[rt] = min(Min[lc], Min[rc]);
}
public: LL query_min(int rt, int l, int r, int ql, int qr) {
if (r < ql || l > qr) return 1e18;
if (l >= ql && r <= qr) return Min[rt];
LL tmp1 = query_min(lc, l, mid, ql, qr);
LL tmp2 = query_min(rc, mid+1, r, ql, qr);
return min(tmp1, tmp2);
}
public: LL query_max(int rt, int l, int r, int ql, int qr) {
if (r < ql || l > qr) return -1e18;
if (l >= ql && r <= qr) return Max[rt];
LL tmp1 = query_max(lc, l, mid, ql, qr);
LL tmp2 = query_max(rc, mid+1, r, ql, qr);
return max(tmp1, tmp2);
}
}Pre, Suf;
int main () {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
pre[i] = pre[i-1] + a[i];
}
for (int i = n; i >= 1; --i) {
suf[i] = suf[i+1] + a[i];
}
ST(n);
Pre.build(1, 1, n, pre);
Suf.build(1, 1, n, suf);
LL ans = -1e18;
for (int i = 1; i <= n; ++i) {
int left, right, l = i, r = n;
while (l <= r) {
if (RMQ(l, mid) < a[i]) r = mid - 1;
else l = mid + 1;
}
right = r;
l = 1, r = i;
while (l <= r) {
if (RMQ(mid, r) < a[i]) l = mid + 1;
else r = mid - 1;
}
left = l;
if (a[i] >= 0) {
LL R = Pre.query_max(1, 1, n, i, right);
LL L = Suf.query_max(1, 1, n, left, i);
ans = max(ans, (R - pre[i-1] + L - suf[i]) * a[i]);
}else {
LL R = Pre.query_min(1, 1, n, i, right);
LL L = Suf.query_min(1, 1, n, left, i);
ans = max(ans, (R - pre[i-1] + L - suf[i]) * a[i]);
}
}
cout << ans << endl;
return 0;
}