题意:给出n个数,现在可以移动一个数的位置,现在要使和sigma(ai*i)最大,询问这个最大和。
思路:将一个数向左移动和向右移动是一样的,现在考虑向左移动。
先预处理出前缀和,将一个数向左移动后,那么改变量为sum[r-1]-sum[l-1]+a[r]*(r-l),考虑枚举r,那么和r有关的数据就变成了常量。
现在问题转化成了求a[r]*l-sum[l-1],注意到这里l和sum[l-]都是递增的,所以可以考虑用斜率优化来加速dp,
维护一个下凸曲线,然后对于每一个a[r],二分斜率或者三分截距就可以解决当前位置左移的最大值。
#include<bits/stdc++.h>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define pb push_back
#define mp make_pair
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int MAXN = 200020;
const LL INF = 1e17;
int n;
int a[MAXN];
LL sumv[MAXN];
int Q[MAXN], h, t;
struct Point {
LL x, y;
Point(LL _x = 0, LL _y = 0) : x(_x), y(_y) {}
} p[MAXN];
LL getY(int k, int j) {
return p[k].y-p[j].y;
}
LL getX(int k, int j) {
return p[k].x-p[j].x;
}
bool check1(int k, int j) {
return getY(Q[k+1], Q[k]) >= getX(Q[k+1], Q[k])*a[j];
}
bool check2(int k, int j) {
return getY(Q[k], Q[k+1]) <= getX(Q[k], Q[k+1])*a[j];
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d", &n);
LL ans = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sumv[i] = sumv[i-1] + a[i];
ans += (LL)i * a[i];
}
for (int i = 1; i <= n; i++)
p[i] = Point(i, sumv[i-1]);
LL tmp = ans;
t = 0;
for (int i = 2; i <= n; i++) {
while (t > 1 && getY(i-1, Q[t])*getX(Q[t], Q[t-1]) <= getY(Q[t], Q[t-1])*getX(i-1, Q[t]))
t--;
Q[++t] = i-1;
int l = 1, r = t;
while (l < r) {
int mid = (l+r) >> 1;
if (check1(mid, i)) r = mid;
else l = mid + 1;
}
ans = max(ans, tmp+(LL)a[i]*Q[r]-sumv[Q[r]-1]+sumv[i-1]-(LL)i*a[i]);
}
t = 0;
for (int i = 1; i <= n; i++)
p[i] = Point(i, sumv[i]);
for (int i = n-1; i > 0; i--) {
while (t > 1 && getY(Q[t], i+1)*getX(Q[t-1], Q[t]) >= getY(Q[t-1], Q[t])*getX(Q[t], i+1))
t--;
Q[++t] = i+1;
int l = 1, r = t;
while (l < r) {
int mid = (l+r) >> 1;
if (check2(mid, i)) r = mid;
else l = mid + 1;
}
ans = max(ans, tmp+(LL)Q[r]*a[i]-(LL)i*a[i]-sumv[Q[r]]+sumv[i]);
}
printf("%I64d", ans);
return 0;
}