这个题最优策略一定是向左上走到某一列再往上一直走。
n*q在线暴力可做,离线按y排序,单调栈维护凸壳。
具体来说:对于i<j若A[i]>A[j] 即j的斜率小而且纵截距小,一定比i优,并且易得栈里直线的交点一定是递增的。
然后直接在交点二分即可。
1 #include<cstdio> 2 #include<algorithm> 3 #define N 500005 4 #define int long long 5 using namespace std; 6 int s[N],top,a[N],b[N],tot,sum[N],n; 7 double jk[N]; 8 double Get(int i,int j){return 1.0*(b[i]-b[j])/(a[j]-a[i]);} 9 inline int read() 10 { 11 int x=0,f=1;char c=getchar(); 12 while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} 13 while(c>='0'&&c<='9')x=x*10+c-48,c=getchar(); 14 return x*f; 15 } 16 struct query{ 17 int x,y,id; 18 inline void init(){x=read(),y=read();} 19 inline friend bool operator < (const query a,const query b){return a.y<b.y;} 20 }Q[N]; 21 int ans[N]; 22 inline void update(int pos) 23 { 24 while(top&&a[s[top]]>=a[pos])top--; 25 while(top>1&&Get(s[top],s[top-1])<Get(pos,s[top]))top--; 26 s[++top]=pos; 27 if(top>1)jk[n-top]=Get(s[top],s[top-1]); 28 return ; 29 } 30 signed main() 31 { 32 n=read(); 33 for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i],b[i]=i*a[i]-sum[i]; 34 int q=read(),j=1; 35 for(int i=1;i<=q;i++) Q[i].init(),Q[i].id=i; 36 sort(Q+1,Q+q+1); 37 for(int i=1;i<=q;i++) 38 { 39 while(j<=Q[i].y)update(j),++j; 40 int now=lower_bound(jk+n-top,jk+n-1,Q[i].x-Q[i].y)-jk;now=n-now; 41 ans[Q[i].id]=a[s[now]]*(Q[i].x-Q[i].y)+b[s[now]]+sum[Q[i].y]; 42 } 43 for(int i=1;i<=q;i++)printf("%lld ",ans[i]); 44 return 0; 45 }