2019南昌邀请赛网络预选赛 I. Max answer(单调栈+暴力??)
题意:
给你你一序列 a,共 n 个元素,求最大的F(l,r);
F(l,r) = (a[l]+a[l+1]+.....+a[r])*min(l,r);
([l,r]的区间和*区间最小值,F(l,r)是我单独定义的,为了方便理解);
我的思路:
分两部分来(看这篇文章的童鞋请先戳这篇文章👉):
1.区间最小值为非负数,此区间一定不包含负数,直接用单调栈求出以某个非负数a[i]为最小值的左右区间L[i],R[i];
然后答案取 ∀i ∈[1,n],a[i] ≥ 0, max{ a[i]*区间和};
2.区间最小值为负数,此时,此区间可能包含负数,还是先用单调栈求出以某个负数a[i]为最小值的左右区间L[i],R[i];
定义 minL[ i ] 为区间 j ∈[ L[i] , i ] 的最小的 a[ j ]+a[ j+1 ]+.....+a[ i ];
然后,预处理出 ∀i ∈[1,n],a[i] < 0, 的minL[ i ];
对于∀i ∈[1,n],a[i] < 0, j 每次从 i+1 到 R[i] 遍历一遍,答案取 max{ a[i]*(minL[i]+a[ j ]) }
最后,答案取这两种情况的最大值;
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define ll long long 6 #define mem(a,b) memset(a,b,sizeof(a)) 7 const int maxn=5e5+50; 8 9 int n; 10 int a[maxn]; 11 int L[maxn]; 12 int R[maxn]; 13 int sta[maxn]; 14 ll sum[maxn]; 15 ll minL[maxn]; 16 17 void F()//单调栈,求出L[],R[]; 18 { 19 int index=0; 20 for(int i=1;i <= n;++i) 21 { 22 while(index > 0 && a[i] <= a[sta[index]]) 23 index--; 24 if(index == 0) 25 L[i]=1; 26 else 27 L[i]=sta[index]+1; 28 sta[++index]=i; 29 } 30 index=0; 31 for(int i=n;i >= 1;--i) 32 { 33 while(index > 0 && a[i] <= a[sta[index]]) 34 index--; 35 if(index == 0) 36 R[i]=n; 37 else 38 R[i]=sta[index]-1; 39 sta[++index]=i; 40 } 41 } 42 ll Solve() 43 { 44 F(); 45 mem(minL,0); 46 for(int i=1;i <= n;++i) 47 { 48 if(a[i] >= 0) 49 continue; 50 for(int j=L[i];j <= i;++j) 51 minL[i]=min(minL[i],sum[i]-sum[j-1]);//预处理出负数a[i]的minL[i] 52 } 53 ll ans=0; 54 //两部分取最大值 55 for(int i=1;i <= n;++i) 56 { 57 if(a[i] >= 0) 58 { 59 ans=max(ans,a[i]*(sum[R[i]]-sum[L[i]-1])); 60 continue; 61 } 62 ans=max(ans,a[i]*minL[i]); 63 for(int j=i+1;j <= R[i];++j) 64 ans=max(ans,a[i]*(minL[i]+sum[j]-sum[i])); 65 } 66 return ans; 67 } 68 int main() 69 { 70 while(~scanf("%d",&n)) 71 { 72 sum[0]=0; 73 for(int i=1;i <= n;++i) 74 { 75 scanf("%d",a+i); 76 sum[i]=sum[i-1]+a[i]; 77 } 78 printf("%lld\n",Solve()); 79 } 80 return 0; 81 }