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);
} 

  

posted @ 2019-05-17 14:54  维和战艇机  阅读(290)  评论(0编辑  收藏  举报