题解
枚举每个区间右端点,如果再枚举左端点则复杂度为O(N^2)不可行。使用斜率优化。
令s为前缀和,p[i] = ∑(i * s[i]),区间[j, i]以i为右端点时区间和可以表示为p[i] - p[j - 1] - (j - 1) * (s[i] - s[j - 1])。
斜率方程(k < j < i) j比k优 为了表示方便j=j-1, k=k-1,((j * s[j] - p[j]) - (k * s[k] - p[k])) / (j - k) > s[i]
由于前缀和s不单调所以不能直接将队首节点弹出,队列q维护斜率单调下降凸壳,在整个队列上进行三分查找区间和的最大值。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
ll a[N], s[N], p[N]; //s前缀和 p[i] = ∑(i * s[i])
ll f[N], q[N], tail; //f[i]以i为结尾的最大子段和 q维护斜率单调下降凸壳
/*
以i为最右端点的区间[i, j]的和
f[i] = p[i] - p[j - 1] - (j - 1) * (s[i] - s[j - 1])
斜率方程(k < j < i) j比k优 为了表示方便j=j-1, k=k-1
((j * s[j] - p[j]) - (k * s[k] - p[k])) / (j - k) > s[i]
*/
inline ll Y(int j)
{
return j * s[j] - p[j];
}
inline ll X(int j)
{
return j;
}
inline ll cross(int k, int j, int i)
{
//double A = 1.0 * (Y(k) - Y(j)) / (k - j);
//double B = 1.0 * (Y(j) - Y(i)) / (j - i);
return (Y(k) - Y(j)) * (j - i) - (Y(j) - Y(i)) * (k - j);
}
inline ll calc(int j, int i)
{
return p[i] - p[j] - j * (s[i] - s[j]);
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
scanf("%lld", &a[i]), s[i] = s[i - 1] + a[i], p[i] = p[i - 1] + i * a[i];
memset(f, -0x3f, sizeof(f));
ll ans = -LINF;
q[tail++] = 0; //1位置可以从1-1转移
for (int i = 1; i <= n; ++i)
{
int l = 0, r = tail - 1;
while (l + 2 < r) //s不单调 三分找区间和最大值
{
int lm = l + (r - l) / 3, rm = r - (r - l) / 3;
if (calc(q[lm], i) > calc(q[rm], i))
r = rm;
else
l = lm;
}
for (int j = l; j <= r; ++j)
f[i] = max(f[i], calc(q[j], i));
ans = max(ans, f[i]);
while (tail > 1 && cross(q[tail - 2], q[tail - 1], i) <= 0)
--tail;
q[tail++] = i;
}
cout << ans << endl;
return 0;
}