Codeforces 631E 斜率优化
题意:给你一个数组,你可以选择数组中的一个数,把它插入数组的其它位置,问∑ i * a[i]的最大值为多少?
思路:设dp[i]表示把第i个数向左边插入可以获得的最大增量,我们假设向左边插入,设插入的位置是j,当前位置是i,那么变化为sum[i - 1] - sum[j - 1] - (i - j) * a[i], 将式子转化,sum[j - 1] = a[i] * j - dp[i] + sum[i - 1] - i * a[i],我们要让dp[i]最大,即让-dp[i]最小,用单调队列维护下凸壳,查询的时候二分斜率即可。向右边插入同理。
代码:
#include <bits/stdc++.h> #define LL long long using namespace std; const int maxn = 200010; LL q[maxn], l, r; LL a[maxn], sum[maxn]; LL dp[maxn]; int binary_search(LL k) { if(r == l) return q[l]; int L = l, R = r; while(L < R) { int mid = (L + R) >> 1; int tmp = q[mid], tmp1 = q[mid + 1]; if(sum[tmp1 - 1] - sum[tmp - 1] <= k * (tmp1 - tmp)) L = mid + 1; else R = mid; } return q[L]; } int binary_search1(LL k) { if(r == l) return q[l]; int L = l, R = r; while(L < R) { int mid = (L + R) >> 1; int tmp = q[mid], tmp1 = q[mid + 1]; if(sum[tmp1] - sum[tmp] <= k * (tmp1 - tmp)) L = mid + 1; else R = mid; } return q[L]; } int main() { int n; LL res = 0; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); sum[i] = sum[i - 1] + a[i]; res = res + a[i] * i; } l = 1, r = 1, q[l] = 1, dp[1] = 0; for (LL i = 2; i <= n; i++) { int pos = binary_search(a[i]); dp[i] = sum[i - 1] - sum[pos - 1] - (i - pos) * a[i]; while(l < r && (sum[q[r] - 1] - sum[q[r - 1] - 1]) * (i - q[r - 1]) >= (sum[i - 1] - sum[q[r - 1] - 1]) * (q[r] - q[r - 1]))r--; q[++r] = i; } LL ans = -5e18; for (int i = 1; i <= n; i++) ans = max(ans, res + dp[i]); l = 1, r = 1, q[1] = n; dp[n] = 0; for (LL i = n - 1; i >= 1; i--) { int pos = binary_search1(a[i]); dp[i] = sum[i] - sum[pos] - (i - pos) * a[i]; while(l < r && (sum[q[r - 1]] - sum[i]) * (q[r] - i) <= (sum[q[r]] - sum[i]) * (q[r - 1] - i))r--; q[++r] = i; } for (int i = 1; i <= n; i++) ans = max(ans, res + dp[i]); ans = max(ans, res); printf("%lld\n", ans); }