2019南昌邀请赛网络赛 I. Max answer

本文探讨了一种解决含有负数的数组中寻找最大乘积区间的算法。通过使用单调栈来处理正数区间的最大乘积问题,并利用两个ST表来维护包含负数的最小和区间,从而找到最大乘积。文章详细介绍了算法的实现过程和关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题麻烦之处在于有负数,对于每一段连续的正数,我们假设当前最小的数字是a[i],那么所能得到最大的value就是包含i的最大区间且最小值是a[i],直接用单调栈维护每一个数字所统治(以他为最小值)的最大区间范围。

负数的话,对于一个很小的负数,这个区间我们希望他的和越小越好,那么乘起来最大值就会越大,所以我们要想怎么找到包含这个点的和最小的区间,一开始我想用two point ,后来越想越怪。最后我发现一段区间和等于sum[j]-sum[k],那么要求包含a[i]的和最小的区间和就是在i到n之间找一个最小的sum[j],在i-1到0之间找一个最大的sum[k],那么sum[j]-sum[k]就会最小了,虽然a[i]在(k+1)-j这段区间可能不是最小值,但是我们扫过去一定能找到最小值。于是用2个st表维护就行了。

考场上写0-i之间找最大的sum[k]也过了。。。数据可能不是很强

据说还可以cdq分治,,我不会啊。。。

#include<bits/stdc++.h>
#define maxl 500010
using namespace std;

int n,top,lg;
long long ans=0;
long long s[maxl];
long long a[maxl],sum[maxl],dol[maxl],dor[maxl];
long long mini[20][maxl],mx[20][maxl];
map <long long,int> mp;
map <long long,int> :: iterator it;

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
	top=0;int l=0,r=0;
	while(l<=n && r<=n)
	{
		while(a[l]<=0 && l<=n)
			l++;
		if(a[l]>0)
		{
			r=l;
			while(a[r+1]>0)
				r++;
			top=0;s[0]=l-1;
			for(int i=l;i<=r;i++)
			{
				while(top>0 && a[i]<a[s[top]])
				{
					dor[s[top]]=i;
					top--;
				}
				s[++top]=i;dol[i]=s[top-1];
			}
			while(top>0)
				dor[s[top]]=r+1,top--;
			for(int i=l;i<=r;i++)
				ans=max(ans,(sum[dor[i]-1]-sum[dol[i]])*a[i]);
		}
		else r=l;
		l=r+1;
	}
}

inline void mainwork()
{
	int lg=log2(n),t;
	for(int i=0;i<=n;i++)
		mx[0][i]=mini[0][i]=sum[i];
	for(int i=1;i<=lg;i++)
		for(int j=0;j<=n;j++)
		{
			mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<(i-1))]);
			mini[i][j]=min(mini[i-1][j],mini[i-1][j+(1<<(i-1))]);
		}
	long long mxx,mii;
	for(int i=1;i<=n;i++)
	if(a[i]<0)
	{
		t=log2(i-0+1);
		mxx=max(mx[t][0],mx[t][i-(1<<t)+1]);
		t=log2(n-i+1);
		mii=min(mini[t][i],mini[t][n-(1<<t)+1]);
		ans=max((mii-mxx)*a[i],ans);
	}
}

inline void print()
{
	printf("%lld",ans);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值