Bear and Bowling 4
Explicación
El editorial oficial usa un enfoque de divide y vencerás. Sin embargo, como muchos problemas de optimización de divide y vencerás, esto también se puede resolver usando el truco de la envolvente convexa.
Primero, definamos lo siguiente:
Queremos encontrar el puntaje de algún subarreglo , definido como
Al expandir esta expresión, podemos expresarla en términos de y :
Para este problema, necesitamos elegir un subarreglo contiguo para maximizar el puntaje requerido. Definamos una función tal que denota el puntaje máximo de un subarreglo que termina en . Así, podemos definir de la siguiente forma:
Reordenamos la ecuación de modo que los términos que dependen solo de queden envueltos entre paréntesis dentro del y los términos que dependen solo de se muevan fuera del . Nótese que hay un único término que depende tanto de como de .
Gracias a esta propiedad, podemos usar el truco de la envolvente convexa. Cada valor se puede representar como una función lineal de la forma
La ecuación original se puede expresar entonces como:
Implementación
Complejidad: . La implementación de abajo usa un Line Container, pero también es posible mantener dinámicamente un deque y usar búsqueda binaria para las consultas aprovechando la monotonía de .
#include <bits/stdc++.h>
using namespace std;
// BeginCodeSnip{Line Container}
template <typename T> T floor_div(T a, T b) { return a / b - ((a ^ b) < 0 && a % b); }
struct LineContainerLine {
mutable long long m, b, p;
bool operator<(const LineContainerLine &o) const { return m < o.m; }
bool operator<(long long x) const { return p < x; }
};
class LineContainer : multiset<LineContainerLine, less<>> {
bool isect(iterator x, iterator y) {
if (y == end()) {
x->p = LLONG_MAX;
return false;
}
if (x->m == y->m) {
x->p = x->b > y->b ? LLONG_MAX : LLONG_MIN;
} else {
x->p = floor_div(y->b - x->b, x->m - y->m);
}
return x->p >= y->p;
}
public:
void add(long long m, long long b) {
auto z = insert({m, b, 0});
auto y = z++;
auto x = y;
while (isect(y, z)) { z = erase(z); }
if (x != begin() && isect(--x, y)) { isect(x, y = erase(y)); }
while ((y = x) != begin() && (--x)->p >= y->p) { isect(x, erase(y)); }
}
long long query(long long x) {
assert(!empty());
auto l = *lower_bound(x);
return l.m * x + l.b;
}
};
// EndCodeSnip
const int MAXN = 2e5 + 1;
int N;
long long A[MAXN], pre[MAXN], ips[MAXN];
int main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> A[i];
pre[i] = pre[i - 1] + A[i];
ips[i] = ips[i - 1] + A[i] * i;
}
LineContainer lc;
long long ans = 0;
for (int i = 1; i <= N; i++) {
lc.add(-i, (i - 1) * pre[i - 1] - ips[i - 1]);
ans = max(ans, lc.query(pre[i]) + pre[i] + ips[i]);
}
cout << ans << '\n';
}