问题1:
ans=max(sum[n]-(sum[i]-sum[j-1])+a[i]*(i-j+1))
=max(sum[n]-sum[i]+sum[j-1]+a[i]*(i+1)-a[i]*j)
=sum[n]-sum[i]+a[i]*(i+1)+f[i]
f[i]=max(-j*a[i]+sum[j-1]),j<i
由于j递增,-j递减,所以从右往左建立凸壳,查询时在凸壳上二分查找即可,时间复杂度$O(n\log n)$。
问题2:
将序列翻转后即化为问题1。
#include<cstdio> #define N 300010 typedef long long ll; int n,i,j,a[N],q[N],t;ll sum[N],b[N],f[N],ans; inline void read(int&a){ char c;bool f=0;a=0; while(!((((c=getchar())>=‘0‘)&&(c<=‘9‘))||(c==‘-‘))); if(c!=‘-‘)a=c-‘0‘;else f=1; while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘; if(f)a=-a; } inline double pos(int x,int y){return (double)(b[x]-b[y])/(double)(x-y);} inline ll ask(int x){ int l=1,r=t-1,fin=t,mid; while(l<=r){ mid=(l+r)>>1; if((double)x>pos(q[mid],q[mid+1]))r=(fin=mid)-1;else l=mid+1; } return b[q[fin]]-(ll)q[fin]*x; } inline void up(ll x){if(ans<x)ans=x;} void work(){ for(ans=-1LL<<60,i=1;i<=n;i++)sum[i]=sum[i-1]+a[i],b[i]=sum[i-1]; for(t=0,i=1;i<=n;q[++t]=i++){ if(i>1)up(sum[n]-sum[i]+(ll)a[i]*(i+1)+ask(a[i])); while(t>1&&pos(i,q[t])>pos(q[t],q[t-1]))t--; } printf("%lld\n",ans); } int main(){ for(read(n),i=1;i<=n;i++)read(a[i]); work(); for(i=1,j=n;i<j;i++,j--)t=a[i],a[i]=a[j],a[j]=t; work(); return 0; }
时间: 2024-10-14 00:56:06