牛客练习赛40 D 小A与最大子段和 斜率优化 三分

题解

枚举每个区间右端点,如果再枚举左端点则复杂度为O(N^2)不可行。使用斜率优化。
令s为前缀和,p[i] = ∑(i * s[i]),区间[j, i]以i为右端点时区间和可以表示为p[i] - p[j - 1] - (j - 1) * (s[i] - s[j - 1])。
斜率方程(k < j < i) j比k优 为了表示方便j=j-1, k=k-1,((j * s[j] - p[j]) - (k * s[k] - p[k])) / (j - k) > s[i]
由于前缀和s不单调所以不能直接将队首节点弹出,队列q维护斜率单调下降凸壳,在整个队列上进行三分查找区间和的最大值。

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
ll a[N], s[N], p[N]; //s前缀和 p[i] = ∑(i * s[i])
ll f[N], q[N], tail; //f[i]以i为结尾的最大子段和 q维护斜率单调下降凸壳
/*
以i为最右端点的区间[i, j]的和
f[i] = p[i] - p[j - 1] - (j - 1) * (s[i] - s[j - 1])
斜率方程(k < j < i) j比k优 为了表示方便j=j-1, k=k-1
((j * s[j] - p[j]) - (k * s[k] - p[k])) / (j - k) > s[i]
*/
inline ll Y(int j)
{
	return j * s[j] - p[j];
}
inline ll X(int j)
{
	return j;
}
inline ll cross(int k, int j, int i)
{
	//double A = 1.0 * (Y(k) - Y(j)) / (k - j);
	//double B = 1.0 * (Y(j) - Y(i)) / (j - i);
	return (Y(k) - Y(j)) * (j - i) - (Y(j) - Y(i)) * (k - j);
}
inline ll calc(int j, int i)
{
	return p[i] - p[j] - j * (s[i] - s[j]);
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
		scanf("%lld", &a[i]), s[i] = s[i - 1] + a[i], p[i] = p[i - 1] + i * a[i];
	memset(f, -0x3f, sizeof(f));
	ll ans = -LINF;
	q[tail++] = 0; //1位置可以从1-1转移
	for (int i = 1; i <= n; ++i)
	{
		int l = 0, r = tail - 1;
		while (l + 2 < r) //s不单调 三分找区间和最大值
		{
			int lm = l + (r - l) / 3, rm = r - (r - l) / 3;
			if (calc(q[lm], i) > calc(q[rm], i))
				r = rm;
			else
				l = lm;
		}
		for (int j = l; j <= r; ++j)
			f[i] = max(f[i], calc(q[j], i));
		ans = max(ans, f[i]);
		while (tail > 1 && cross(q[tail - 2], q[tail - 1], i) <= 0)
			--tail;
		q[tail++] = i;
	}
	cout << ans << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值