sequence(线段树+单调栈) (2019牛客暑期多校训练营(第四场))

示例:

输入:

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 }

 

posted @ 2019-07-29 15:27  Aamir_Dan  阅读(185)  评论(0编辑  收藏  举报