小A与最大子段和
题意
在一个序列 ({a}) 里找到一个非空子段 ({b}), 满足 (sumlimits_{i=1}^{|b|}b_i imes i) 最大
(nle 200000,|a_i|le 2000)
长的都一脸的斜率优化
考虑表示一个子段([l+1,r])为
[egin{aligned}
&sum_{i=l+1}^ra_i(i-l)\
&=sum_{i=l+1}^ra_i imes i-a_i imes l
end{aligned}
]
设(A_k=sum_{i=1}^k a_ii,B_k=sum_{i=1}^k a_i)
那继续化简为
[A_r-B_r imes l+B_l imes l-A_l
]
然后我们枚举(r),想办法搞一下(l)
维护一个((i,B_i imes i-A_i))的上凸包,然后发现是对斜率询问最大截距,直接在凸包上三分就可以了。
Code:
#include <cstdio>
#include <algorithm>
#define ll long long
const int N=2e5+10;
using std::max;
int n,tot;
ll A[N],B[N];
struct Point
{
ll x,y;
Point(){}
Point(ll X,ll Y){x=X,y=Y;}
Point friend operator -(Point a,Point b){return Point(a.x-b.x,a.y-b.y);}
ll cal(ll d){return d*x+y;}
}s[N];
ll Cross(Point a,Point b){return a.x*b.y-a.y*b.x;}
void ins(Point a)
{
while(tot>1&&Cross(a-s[tot],s[tot]-s[tot-1])<=0) --tot;
s[++tot]=a;
}
int main()
{
scanf("%d",&n);
ins(Point(0,0));
ll ans=-(1ll<<50),a;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a);
A[i]=A[i-1]+a*i;
B[i]=B[i-1]+a;
int l=1,r=tot;
while(l<r)
{
int mid=l+r>>1;
if(s[mid].cal(-B[i])<s[mid+1].cal(-B[i])) l=mid+1;
else r=mid;
}
ans=max(ans,A[i]+s[l].cal(-B[i]));
ins(Point(i,B[i]*i-A[i]));
}
printf("%lld
",ans);
return 0;
}
2019.2.16