题目
题目链接: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;
}