原题解链接:https://ac.nowcoder.com/discuss/154293

Ax=i=1xai×i,Bx=i=1xaiA_{x}=\sum_{i=1}^{x} a_{i} \times i, B_{x}=\sum_{i=1}^{x} a_{i}

fif_i ​ 表示以i i为右端点的 “最大子段和”,假设左端点为j+1 j+1

fi=k=j+1iak×(kj)f_{i}=\sum_{k=j+1}^{i} a_{k} \times(k-j)

=AiAj(BiBj)×j=A_{i}-A_{j}-\left(B_{i}-B_{j}\right) \times j

=Aij×BiAj+j×Bj=A_{i}-j \times B_{i}-A_{j}+j \times B_{j}

y=j×BjAj,x=j,k=Bi,b=fiAiy=j \times B_{j}-A_{j}, x=j, k=B_{i}, b=f_{i}-A_{i}

可得y=kx+b y=kx+b,可以使用斜率优化

希望最大化截距,且横坐标x x单调,斜率k k不单调,所以用单调栈维护凸壳,在凸壳上三分DPDP值/二分斜率

另有一种k k单调,xx不单调的推法(鸣谢验题人)

fif_i ​ 表示以 ii 为左端点的"最大子段和"

si=j=inaj,ci=j=insjs_i=\sum_{j=i}^na_j, c_i=\sum_{j=i}^ns_j

fi=j=ik1sjsk=cicksk×(ki)f_{i}=\sum_{j=i}^{k-1} s_{j}-s_{k}=c_{i}-c_{k}-s_{k} \times(k-i)

cj+sj×j=i×sj+cific_{j}+s_{j} \times j=i \times s_{j}+c_{i}-f_{i}

y=cj+sj×j,k=i,x=sj,b=cifiy=c_{j}+s_{j} \times j, k=i, x=s_{j}, b=c_{i}-f_{i}

(上面的kk变成jj了)

这里要最小化截距,使用cdqcdq分治

复杂度O(nlogn) O(nlogn) ,时限比较宽松,两个loglog也可以通过

几个关于这个写法代码细节的提示:

  • 排序时只用横坐标当关键字(横坐标相同的情况)
  • 维护凸壳时 \leq或者\geq 写成< <或者>>
  • dpdp数组初值是00(题目中要求非空子段)
#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;
}