题目链接: https://nanti.jisuanke.com/t/38228
题目大意:给你n个数,让你找出一个区间中f的最大值,具体的f计算方法,这段区间的和乘以这段区间的最小值。
具体思路:我们枚举每个位置,对于当前位置的数,通过二分 找出这个数作为区间最小值能够到达的最左端和最右端。如果是正数,我们直接a[i]*这段区间和就可以了,因为都是正数。
如果当前的a[i]是负数,对于这个点的右段,我们找出一个前缀和最小的点,然后对于这个点的左端,我们找出一个前缀和最大的,这样就能保证选定的区间是最小的了,负数*负数=正数。
预处理出前缀和在每段区间的最小值,最大值,以及每个区间中a[i]的最小值。
感谢qyn的讲解。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define inf 0x3f3f3f3f 4 # define ll long long 5 const int maxn = 5e5+100; 6 ll dp1[maxn][21],dp2[maxn][21],dp3[maxn][21]; 7 ll a[maxn]; 8 ll qian[maxn]; 9 int n; 10 void RMQ1() 11 { 12 for(int i=1; i<=20; i++) 13 { 14 for(int j=1; j<=n; j++) 15 { 16 if(j+(1<<i)-1<=n) 17 { 18 dp1[j][i]=min(dp1[j][i-1],dp1[j+(1<<(i-1))][i-1]); 19 } 20 } 21 } 22 } 23 void RMQ2() 24 { 25 for(int i=1; i<=20; i++) 26 { 27 for(int j=1; j<=n; j++) 28 { 29 if(j+(1<<i)-1<=n) 30 { 31 dp2[j][i]=max(dp2[j][i-1],dp2[j+(1<<(i-1))][i-1]); 32 } 33 } 34 } 35 } 36 void RMQ3() 37 { 38 for(int i=1; i<=20; i++) 39 { 40 for(int j=1; j<=n; j++) 41 { 42 if(j+(1<<i)-1<=n) 43 { 44 dp3[j][i]=min(dp3[j][i-1],dp3[j+(1<<(i-1))][i-1]); 45 } 46 } 47 } 48 } 49 bool judge(int l,int r,ll val) 50 { 51 int k=0; 52 k=(int)log2((double)(r-l+1)); 53 ll tmp=min(dp1[l][k],dp1[r-(1<<k)+1][k]); 54 return tmp==val; 55 } 56 int Find_l(int pos) 57 { 58 int l=1,r=pos; 59 int ans=pos; 60 while(l<=r) 61 { 62 int mid=(l+r)>>1; 63 if(judge(mid,pos,a[pos])) 64 { 65 ans=mid; 66 r=mid-1; 67 } 68 else 69 l=mid+1; 70 } 71 return ans; 72 } 73 int Find_r(int pos) 74 { 75 int l=pos,r=n; 76 int ans=pos; 77 while(l<=r) 78 { 79 int mid=(l+r)>>1; 80 if(judge(pos,mid,a[pos])) 81 { 82 ans=mid; 83 l=mid+1; 84 } 85 else 86 r=mid-1; 87 } 88 return ans; 89 } 90 int get_max(int l,int r) 91 { 92 int k=0; 93 k=(int)log2((double)(r-l+1)); 94 ll tmp=max(dp2[l][k],dp2[r-(1<<k)+1][k]); 95 return tmp; 96 } 97 int get_min(int l,int r) 98 { 99 int k=0; 100 k=(int)log2((double)(r-l+1)); 101 ll tmp=min(dp3[l][k],dp3[r-(1<<k)+1][k]); 102 return tmp; 103 } 104 int main() 105 { 106 scanf("%d",&n); 107 for(int i=1; i<=n; i++) 108 { 109 scanf("%lld",&a[i]); 110 dp1[i][0]=a[i]; 111 qian[i]=qian[i-1]+a[i]; 112 dp2[i][0]=dp3[i][0]=qian[i]; 113 } 114 RMQ1(); /// 区间最小值,在每一次询问的时候求出最左边的端点和最右边的端点 115 RMQ2();/// 前缀和最大值 116 RMQ3(); /// 前缀和最小值 117 ll ans=0; 118 for(int i=1; i<=n; i++) 119 { 120 int t1=Find_l(i); 121 int t2=Find_r(i); 122 if(a[i]>0) 123 ans=max(ans,(qian[t2]-qian[t1-1])*a[i]); 124 else 125 { 126 ans=max(ans,a[i]*(get_min(i,t2)-get_max(t1,i))); 127 } 128 } 129 printf("%lld\n",ans); 130 return 0; 131 }