【计蒜客】南昌网络赛 Max answer

题目链接:https://nanti.jisuanke.com/t/38228

题意:给定数组,选择一个区间,使区间的最小值 乘 区间和 最大。

题解:

单调栈+ST表

因为存在负数,所以无法直接使用单调栈。可以先使用单调栈找出以当前数为最小值的左右区间。再使用ST表用前缀和和后缀和求出最值,之后再判断取最大值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll; 
const int maxn = 5e5 + 10;
ll a[maxn];
ll b[maxn];
ll ans[maxn];
ll l[maxn], r[maxn];
ll po2[20], lg2[maxn], mn[20][maxn];//po2[i]表示2的i次方 lg2[i]表示log(i) mn[i][j]表示从j到j+2^i-1的最大值 
void ST(ll a[], int n)//数组 长度 
{
	po2[0] = 1, lg2[0] = -1;
	for (int i = 1; i < 20; i++)
		po2[i] = po2[i - 1] * 2;
	for (int i = 1; i < maxn; i++)
		lg2[i] = lg2[i / 2] + 1;
	for (int i = 1; i <= n; i++)//显然i到i+2^0-1就i一个位置,那么最大值等于自己本身的值
		mn[0][i] = a[i];
	for (int i = 1; i <= lg2[n]; i++)
		for (int j = 1; j <= n; j++)
			if (j + po2[i] - 1 <= n)
				mn[i][j] = max(mn[i - 1][j], mn[i - 1][j + po2[i - 1]]);//状态继承
}
ll ask(ll l, ll r)//2^log(a)>a/2 
{
	ll t = lg2[r - l + 1];
	return max(mn[t][l], mn[t][r - po2[t] + 1]);
}
void ST1(ll a[], int n)//数组 长度 
{
	po2[0] = 1, lg2[0] = -1;
	for (int i = 1; i < 20; i++)
		po2[i] = po2[i - 1] * 2;
	for (int i = 1; i < maxn; i++)
		lg2[i] = lg2[i / 2] + 1;
	for (int i = 1; i <= n; i++)//显然i到i+2^0-1就i一个位置,那么最小值等于自己本身的值
		mn[0][i] = a[i];
	for (int i = 1; i <= lg2[n]; i++)
		for (int j = 1; j <= n; j++)
			if (j + po2[i] - 1 <= n)
				mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + po2[i - 1]]);//状态继承
}
ll ask1(ll l, ll r)//2^log(a)>a/2 
{
	ll t = lg2[r - l + 1];
	return min(mn[t][l], mn[t][r - po2[t] + 1]);
}
int main()
{
	ll  MAX = 0;
	ll n;
	cin >> n;
	for (int i = 1; i <= n; i++)	cin >> a[i];

	stack<ll > s;
	for (int i = 1; i <= n; i++)//向左遍历的第一个比它小的元素的位置(使用单调递增栈)
	{
		while (!s.empty() && 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.empty() && a[s.top()] >= a[i])	s.pop();//加等号后,为小于等于的数
		if (s.empty())	r[i] = n + 1;
		else	r[i] = s.top();
		s.push(i);
	}
	while (!s.empty())	s.pop();
	
	memset(b, 0, sizeof(b));
	for (int i = 1; i <= n; i++)	b[i] = b[i - 1] + a[i];
	ST(b, n);
	for (int i = 1; i <= n; i++)
	{
		if (a[i] > 0)
		{
			ans[i] += ask(i,r[i]-1)-b[i];
		}
	}
	memset(b, 0, sizeof(b));
	for (int i = n; i >= 1; i--)	b[i] = b[i + 1] + a[i];
	ST(b, n);
	for (int i = 1; i <= n; i++)
	{
		if (a[i] > 0)
		{
			ans[i] += ask(l[i]+1, i) - b[i];
			ans[i] += a[i];
		}
	}
	memset(b, 0, sizeof(b));
	for (int i = 1; i <= n; i++)	b[i] = b[i - 1] + a[i];
	ST1(b, n);
	for (int i = 1; i <= n; i++)
	{
		if (a[i] < 0)
		{
			ans[i] += ask1(i, r[i] - 1)-b[i];
		}
	}
	memset(b, 0, sizeof(b));
	for (int i = n; i >= 1; i--)	b[i] = b[i + 1] + a[i];
	ST1(b, n);
	for (int i = 1; i <= n; i++)
	{
		if (a[i] < 0)
		{
			ans[i] += ask1(l[i] + 1, i)-b[i];
			ans[i] += a[i];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		MAX = max(MAX, ans[i] * a[i]);
	}
	cout << MAX << endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值