示例:
输入:
3
1 -1 1
1 2 3
输出:
3
题意:求最大的(a区间最小值*b区间和)
线段树做法:用单调栈求出每个数两边比b数组大的左右边界,然后用线段树求出每段区间的和sum、最小前缀lsum、最小后缀rsum,枚举每个数a[i],设以a[i]为最小值的区间为[l,r]
若a[i]>0,则最优解就是a[i]*(b数组[l,r]的区间和),因为a数组[l,r]上的数都比a[i]大。
若a[i]<0,则最优解是a[i]*(b数组[l,i-1]上的最小后缀+b[i]+b数组[i+1,r]上的最小前缀),在线段树上查询即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=3e6+10,inf=0x3f3f3f3f; 5 ll n,a[N],b[N],ls[N],rs[N],sta[N],tp; 6 ll sum[N],lsum[N],rsum[N]; 7 void ttbuild() { 8 a[n+1]=~inf,sta[tp=0]=n+1; 9 for(ll i=1; i<=n; ++i) { 10 for(; a[i]<a[sta[tp]]; --tp); 11 ls[i]=rs[sta[tp]],rs[sta[tp]]=i,sta[++tp]=i; 12 } 13 } 14 void dfs(ll u) { 15 if(!u)return; 16 dfs(ls[u]),dfs(rs[u]); 17 sum[u]=sum[ls[u]]+b[u]+sum[rs[u]]; 18 lsum[u]=min(lsum[ls[u]],sum[ls[u]]+b[u]+lsum[rs[u]]); 19 rsum[u]=min(rsum[rs[u]],sum[rs[u]]+b[u]+rsum[ls[u]]); 20 } 21 int main() { 22 scanf("%lld",&n); 23 for(ll i=1; i<=n; ++i)scanf("%lld",&a[i]); 24 for(ll i=1; i<=n; ++i)scanf("%lld",&b[i]); 25 ttbuild(),dfs(rs[n+1]); 26 ll ans=0; 27 for(ll i=1; i<=n; ++i) { 28 if(a[i]>0)ans=max(ans,a[i]*sum[i]); 29 else if(a[i]<0)ans=max(ans,a[i]*(rsum[ls[i]]+b[i]+lsum[rs[i]])); 30 } 31 printf("%lld\n",ans); 32 return 0; 33 }