原题解链接:https://ac.nowcoder.com/discuss/154293
令
表示以为右端点的 “最大子段和”,假设左端点为
则
可得,可以使用斜率优化
希望最大化截距,且横坐标单调,斜率不单调,所以用单调栈维护凸壳,在凸壳上三分值/二分斜率
另有一种单调,不单调的推法(鸣谢验题人)
表示以 为左端点的"最大子段和"
令
(上面的变成了)
这里要最小化截距,使用分治
复杂度 ,时限比较宽松,两个也可以通过
几个关于这个写法代码细节的提示:
- 排序时只用横坐标当关键字(横坐标相同的情况)
- 维护凸壳时或者 写成或者
- 数组初值是(题目中要求非空子段)
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 2e5 + 5;
ll a[N], c[N], dp[N];
int n, q[N], l, r, t[N];
ll f(int x) {
return x * a[x] + c[x];
}
ll g(int x) {
return a[x];
}
bool cmp(int x, int y) {
return g(x) < g(y) || (g(x) == g(y) && f(x) > f(y));
}
void solve(int L, int R) {
if (L == R) {
dp[L] = a[L] - a[L + 1];
return;
}
int mid = (L + R) >> 1;
solve(L, mid); solve(mid + 1, R);
l = r = 0;
sort(t + mid + 1, t + R + 1, cmp);
for (int i = mid + 1; i <= R; i++) {
while (l + 1 < r) {
if (g(t[i]) == g(q[r - 1]) || (f(t[i]) - f(q[r - 1])) * (g(q[r - 1]) - g(q[r - 2])) <= (f(q[r - 1]) - f(q[r - 2])) * (g(t[i]) - g(q[r - 1]))) --r;
else break;
}
q[r++] = t[i];
}
sort(t + L, t + mid + 1);
for (int i = L; i <= mid; i++) {
int p = t[i];
while (l + 1 < r && f(q[l + 1]) - f(q[l]) <= p * (g(q[l + 1]) - g(q[l]))) l++;
dp[p] = max(dp[p], c[p] - c[q[l]] - (q[l] - p) * a[q[l]]);
}
}
int main() {
cin >> n; t[n + 1] = n + 1;
assert(n > 0 && n <= 200000);
for (int i = 1; i <= n; i++) cin >> a[i], t[i] = i, assert(a[i] >= -2000 && a[i] <= 2000);
for (int i = n; i; i--) a[i] += a[i + 1];
for (int i = n; i; i--) c[i] = a[i] + c[i + 1];
solve(1, n + 1);
cout << *max_element(dp + 1, dp + 1 + n);
return 0;
}