2019牛客多校训练第四场C.sequence(线段树+单调栈+前缀和)

题目传送门

题意

输入整数n,给出两个包含n个整数的序列a和b,找到一个区间[l,r],使在该区间内a序列最小值×b序列区间和的值最大。

题解

遍历a序列,维护单调栈求以当前值a[i]为最小值时的最大可达区间[ L[i],R[i] ](即最大左右边界);

b序列先预处理其前缀和pre_sum,后用线段树维护pre_sum数组的区间最值;所以答案就是a[i]×b序列在[ L[i],R[i] ]区间内的和的最大值,不断更新最优解求最大结果即可。

当a[i]≥0时,b序列在[ L[i],R[i] ]区间内的和的最大值为b序列前缀和数组后半部分最大值-前半部分最小值,即:

max( [pre_sum(i),pre_sum(R[i])] )-min( [pre_sum(L[i]-1),pre_sum(i-1)] );

当a[i]<0时,b序列在[ L[i],R[i] ]区间内的和的最大值为b序列前缀和数组后半部分最小值-前半部分最大值,即:

min( [pre_sum(i),pre_sum(R[i])] )-max( [pre_sum(L[i]-1),pre_sum(i-1)] );

时间复杂度O(nlogn)。

Code

  1 /*2442ms*/
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 typedef long long ll;
  5 const int maxn=3e6+5;
  6 const ll inf=0x3f3f3f3f3f3f3f3fLL;
  7 ll a[maxn],b[maxn],pre_sum[maxn],L[maxn],R[maxn],sta[maxn];
  8 struct node
  9 {
 10     int l,r;//区间[l,r]
 11     ll mx;//区间最大值
 12     ll mn;//区间最小值
 13 }tree[maxn<<2];//一定要开到4倍多的空间
 14 void pushup(int index)
 15 {
 16     tree[index].mx=max(tree[index<<1].mx,tree[index<<1|1].mx);
 17     tree[index].mn=min(tree[index<<1].mn,tree[index<<1|1].mn);
 18 }
 19 void build(int l,int r,int index)
 20 {
 21     tree[index].l=l;
 22     tree[index].r=r;
 23     if(l==r){
 24         tree[index].mn=tree[index].mx=pre_sum[l];
 25         return;
 26     }
 27     int mid=(l+r)>>1;
 28     build(l,mid,index<<1);
 29     build(mid+1,r,index<<1|1);
 30     pushup(index);
 31 }
 32 ll queryMIN(int l,int r,int index)
 33 {
 34     if(l<=tree[index].l&&r>=tree[index].r)
 35         return tree[index].mn;
 36     int mid=(tree[index].l+tree[index].r)>>1;
 37     ll Min=inf;
 38     if(l<=mid)
 39         Min=min(queryMIN(l,r,index<<1),Min);
 40     if(r>mid)
 41         Min=min(queryMIN(l,r,index<<1|1),Min);
 42     return Min;
 43 }
 44 ll queryMAX(int l,int r,int index)
 45 {
 46     if(l<=tree[index].l&&r>=tree[index].r)
 47         return tree[index].mx;
 48     int mid=(tree[index].l+tree[index].r)>>1;
 49     ll Max=-inf;
 50     if(l<=mid)
 51         Max = max(queryMAX(l,r,index<<1),Max);
 52     if(r>mid)
 53         Max = max(queryMAX(l,r,index<<1|1),Max);
 54     return Max;
 55 }
 56 int main()
 57 {
 58     int n;
 59     while(~scanf("%d",&n))
 60     {
 61         for(int i=1;i<=n;i++)
 62             scanf("%lld",&a[i]);
 63         for(int i=1;i<=n;i++){
 64             scanf("%lld",&b[i]);
 65             pre_sum[i]=pre_sum[i-1]+b[i];//求b序列前缀和
 66         }
 67         build(0,n,1);
 68         /*单调栈求左端点L[i]*/
 69         int top=0;
 70         for(int i=1;i<=n;i++){
 71             while(top&&a[i]<=a[sta[top]])
 72                 top--;
 73             L[i]=(top==0)?1:sta[top]+1;
 74             sta[++top]=i;
 75         }
 76          /*单调栈求右端点R[i]*/
 77         top=0;
 78         for(int i=n;i>=1;i--){
 79             while(top&&a[i]<=a[sta[top]])
 80                 top--;
 81             R[i]=(top==0)?n:sta[top]-1;
 82             sta[++top]=i;
 83         }
 84         
 85         ll ans=-inf;
 86         for(int i=1;i<=n;i++){
 87             ll cnt=0;
 88             if(a[i]>0){
 89                 cnt=(queryMAX(i,R[i],1)-queryMIN(L[i]-1,i-1,1))*a[i];
 90             }
 91             else if(a[i]<0){
 92                 cnt=(queryMIN(i,R[i],1)-queryMAX(L[i]-1,i-1,1))*a[i];
 93             }
 94             else if(a[i]==0)
 95                 cnt=0;
 96             if(cnt>ans)ans=cnt;
 97         }
 98         printf("%lld\n",ans);
 99     }
100     return 0;
101 }
102 
103 /*
104 7
105 -519 9794 1664 8189 -295 3471 1104
106 -1796 -3137 7098 -3333 4683 -3968 467 3
107 11811072
108 */
109 /*
110 18
111 -273 7169 -8145 -6664 -2970 698 8701 -6791 459 -5678 -846 8072 6384 5051 -3606 -4737 -9928 6815
112 1609 -8810 9205 -9129 -1919 -116 -6752 385 4347 614 6261 4399 -8961 1022 -1360 -5074 7825 -122
113 142708545
114 */
View Code

猜你喜欢

转载自www.cnblogs.com/HOLLAY/p/11374517.html