Max answer (线段树加单调栈(最小子段和))

https://nanti.jisuanke.com/t/38228

题意:求最大值:连续区间和*区间最小值

          求最小值时用单调栈求该值左边第一个小的与右边第一个小的来枚举区间

          又因为存在负数,所以求区间子段和时分两种情况:

          1.该值为正数时  乘以区间即可; 预处理前缀和,最后查询O(1);

          2.该值为负数时  需使区间和最小

          这里用线段树操作找区间最小:

某大佬的区间子段和的解释:https://blog.csdn.net/wu_tongtong/article/details/73385029

线段树需要维护的是:
左端点 x
右端点 y (本人喜欢直接维护端点)
[x,y]内的最大子段和 ms
[x,y]的区间和 s
[x,y]内的紧靠左端点的最大子段和 ls
[x,y]内的紧靠右端点的最大子段和 rs

s的维护很常规,
ls:有两种情况:
1.该区间内的ls是ta左儿子的ls
2.该区间内的ls是左儿子的s+右儿子的ls
同理,rs:有两种情况:
1.该区间内的rs是ta右儿子的rs
2.该区间内的rs是右儿子的s+左儿子的rs
而ms有三种情况:
1.该区间内的ms是左儿子的ms
2.该区间内的ms是右儿子的ms
3.该区间内的ms是左儿子的rs+右儿子的ls 
--------------------- 
作者:Coco_T_ 
来源:CSDN 
原文:https://blog.csdn.net/wu_tongtong/article/details/73385029 
版权声明:本文为博主原创文章,转载请附上博文链接!

所以综上所述最小区间和代码如下:

ll sum[maxn*4+1];// 当 x 为正数时的和 
ll lmin[maxn*4+1];//当 x 为负数时 找以x为左端点的右区间最小和 
ll rmin[maxn*4+1];//同理 找以x为右端点的左区间最小和  
ll pes[maxn];//前缀和  快速操作求区间和O(1); 
void pushup(int x){
	sum[x]=sum[x<<1]+sum[x<<1|1];
	lmin[x]=min(lmin[x<<1],sum[x<<1]+lmin[x<<1|1]);//紧靠以x为左端点的左儿子 和 以x为左端点左儿子区间和+右儿子紧靠x的区间 比较 
	rmin[x]=min(sum[x<<1|1]+rmin[x<<1],rmin[x<<1|1]);//紧靠以x为右端点的右儿子 和  以x为右端点的右儿子区间和和+左儿子紧靠x的区间比较 
}

注意: 数据范围有负数,所以当比较最小值时 ,初始 ans=-inf;

完整代码如下

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=5e5+10;
typedef long long ll;
const ll iinf=0x3f3f3f3f3f3f3f3f;
int a[maxn];
int l[maxn];
int r[maxn];
stack<int> s;
ll sum[maxn*4+1];// 当 x 为正数时的和 
ll lmin[maxn*4+1];//当 x 为负数时 找以x为左端点的右区间最小和 
ll rmin[maxn*4+1];//同理 找以x为右端点的左区间最小和  
ll pes[maxn];//前缀和  快速操作求区间和O(1); 

void pushup(int x){
	sum[x]=sum[x<<1]+sum[x<<1|1];
	lmin[x]=min(lmin[x<<1],sum[x<<1]+lmin[x<<1|1]);//紧靠以x为左端点的左儿子 和 以x为左端点左儿子区间和+右儿子紧靠x的区间 比较 
	rmin[x]=min(sum[x<<1|1]+rmin[x<<1],rmin[x<<1|1]);//紧靠以x为右端点的右儿子 和  以x为右端点的右儿子区间和和+左儿子紧靠x的区间比较 
}
void build(int l,int r,int p){
	if(l==r){
	    sum[p]=lmin[p]=rmin[p]=a[l];
	    return ;
    }
	int mid=(l+r)/2;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	pushup(p);
}
int x;
ll quertL(int L,int R,int l,int r,int p){//找[x,R];固定x为L 紧靠x的右区间 
	if(L<=l&&r<=R){
        return pes[l-1]-pes[x-1]+lmin[p];
	}
	int mid=(l+r)/2;
	ll ans=iinf;
	if(L<=mid){
		ans=min(ans,quertL(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=min(ans,quertL(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
ll quertR(int L,int R,int l,int r,int p){//[L,x];固定x为R  紧靠x的左区间 
	if(L<=l&&r<=R){
		return pes[x]-pes[r]+rmin[p];
	}
	int mid=(l+r)/2;
    ll ans=iinf;
	if(L<=mid){
		ans=min(ans,quertR(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=min(ans,quertR(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pes[i]=a[i]+pes[i-1];//前缀和 
	}
	build(1,n,1);
	while(!s.empty()) s.pop();
	for(int i=1;i<=n;i++){
		while(s.size()&&a[s.top()]>=a[i]) s.pop();
		if(s.empty()) l[i]=0;
		else          l[i]=s.top();
		s.push(i);
	}
	while(!s.empty()) s.pop();
	for(int i=n;i>=1;i--){
		while(s.size()&&a[s.top()]>=a[i]) s.pop();
		if(s.empty())  r[i]=n+1;
		else           r[i]=s.top();
		s.push(i);
	}
	ll ans=-iinf;//注意 
	for(int i=1;i<=n;i++){
		int k=a[i];
		int L=l[i]+1;
		int R=r[i]-1;
		x=i;
		if(k>=0){
		   ans=max(ans,(pes[R]-pes[L-1])*k);	
		}
		else{
			ans=max(ans,(quertL(i,R,1,n,1)+quertR(L,i,1,n,1)-k)*k);
		}  
	}
	cout<<ans<<endl;
}
 

猜你喜欢

转载自blog.csdn.net/gml1999/article/details/89682326