【牛客练习赛40】D - 小A与最大子段和

题目

题目链接:https://ac.nowcoder.com/acm/contest/369/D
在一个序列 \(a\) 里找到一个非空子段 \(b\),使 \(\sum^{m}_{i=1} b[i]\times i\) 最大。

思路

考虑枚举右端点 \(r\)。那么我们需要找到一个左端点 \(l\),使得 \(\sum^{r}_{i=l}a[i]\times (i-l+1)\) 最大。
\(sum[i]=\sum^{i}_{j=1}a[j],spow[i]=\sum^{i}_{j=1}a[j]\times j\),那么

\[\sum^{r}_{i=l}a[i]\times (i-l+1)=spow[r]-spow[l-1]-(l-1)\times (sum[r]-sum[l-1]) \]

所以有

\[ans=max(ans,spow[r]-spow[l-1]-(l-1)\times (sum[r]-sum[l-1])) \]

也就是

\[sum[j]\times j-spow[j]=sum[i]\times j-s[i]+ans \]

我们要让截距最大,那么就将 \((j,sum[j]\times j-spow[j])\) 扔进栈里,维护上凸壳即可。
时间复杂度 \(O(n\log n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=200010;
int n,top,st[N];
ll ans,sum[N],spow[N],X[N],Y[N];

double slope(int x,int y)
{
	if (X[x]==X[y]) return 1000000000000.0;
	return 1.0*(Y[x]-Y[y])/(X[x]-X[y]);
}

int binary(int x)
{
	int l=1,r=top-1,mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (slope(st[mid],st[mid+1])<=1.0*sum[x]) r=mid-1;
			else l=mid+1;
	}
	return st[r+1];
}

int main()
{
	ans=-2333333333333333333LL;
	scanf("%d",&n);
	top=1;
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		sum[i]=sum[i-1]+x;
		spow[i]=spow[i-1]+x*i;
		Y[i]=sum[i]*i-spow[i]; X[i]=i;
		
		int j=binary(i);
		ans=max(ans,spow[i]-spow[j]-(sum[i]-sum[j])*j);
		while (top && slope(st[top-1],st[top])<slope(st[top],i))
			top--;
		st[++top]=i;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2020-06-16 20:53  stoorz  阅读(124)  评论(0编辑  收藏  举报