2019牛客暑期多校第四场C.sequence (单调栈+线段树(区间内最大/最小子段和))

https://ac.nowcoder.com/acm/contest/884/C

----------------------------------------------------------------

和南昌网络赛Max Answer一样,些许变化。

这里讲了线段树具体求区间最小子段和https://blog.csdn.net/gml1999/article/details/89682326、

---------------------------------------------------------------------------------------------------------------------------

题意:给你a.b两个数组,让你求max{min a(l,r)*sum b(l,r)} 1<l<r<n;

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

题解:

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

          当a[i]负数时:需要求b数组区间内最小连续子段和;(只有区间内子段和为负时,才会让乘积最大化)

          当a[i]正数时:需要求b数组区间内最大连续子弹和;(因为b数组区间内有正有负)

          这里用线段树操作找区间和最小/最大:(线段树操作基于a[i]下标的x找)

#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=3e6+10;
typedef long long ll;
const ll iinf=0x3f3f3f3f3f3f3f3f;
int a[maxn];
int b[maxn];
int l[maxn];
int r[maxn];
stack<int> s;
ll sum[maxn*4+1];// 和 
ll lmin[maxn*4+1];//当 x 为负数时 找以x为左端点的右区间最小和 
ll rmin[maxn*4+1];//同理 找以x为右端点的左区间最小和  
ll lmax[maxn*4+1];//当x为正数时,同理 
ll rmax[maxn*4+1];
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的区间比较 
	lmax[x]=max(lmax[x<<1],sum[x<<1]+lmax[x<<1|1]);//同理 
	rmax[x]=max(sum[x<<1|1]+rmax[x<<1],rmax[x<<1|1]);
}
void build(int l,int r,int p){
	if(l==r){
	    rmax[p]=lmax[p]=sum[p]=lmin[p]=rmin[p]=b[l];
	    return ;
    }
	int mid=(l+r)/2;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	pushup(p);
}
int x;//全程是以这个点为端点找左右
ll quertminL(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,quertminL(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=min(ans,quertminL(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
ll quertminR(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,quertminR(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=min(ans,quertminR(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
ll quertmaxL(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]+lmax[p];
	}
	int mid=(l+r)/2;
	ll ans=-iinf;
	if(L<=mid){
		ans=max(ans,quertmaxL(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=max(ans,quertmaxL(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
ll quertmaxR(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]+rmax[p];
	}
	int mid=(l+r)/2;
    ll ans=-iinf;
	if(L<=mid){
		ans=max(ans,quertmaxR(L,R,l,mid,p<<1));
	}
	if(R>mid){
        ans=max(ans,quertmaxR(L,R,mid+1,r,p<<1|1));
	}
	return ans;
}
int main(){
	std::ios::sync_with_stdio(false);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
		pes[i]=b[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(L==R){
			ans=max(ans,(ll)(a[i]*b[i]));
			continue;
		}
		if(k>0){
		    ans=max(ans,(quertmaxL(i,R,1,n,1)+quertmaxR(L,i,1,n,1)-b[i])*k);	
		}else if(k<0){
			ans=max(ans,(quertminL(i,R,1,n,1)+quertminR(L,i,1,n,1)-b[i])*k);
		}else if(k==0){
			ans=max(ans,0ll);
		}
	}
	cout<<ans<<endl;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值