南昌网络赛I.Max answer(单调栈+线段树)

I. Max answer

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1 <= n <= 5*10^5).

Second line contains n integers represent the array a (-10^5 <= ai <= 10^5).

Output

One line contains an integer represent the answer of the array.

题意:

((区间和)乘(区间最小值))的最大值

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

思路:

这个题和POJ2796差不多,就是范围不同。http://poj.org/problem?id=2796

此题可以先用单调栈判断以每个值为最小值的最大左边界和右边界。后面,对每个值分成负数和正数讨论,

①如果为正数,取区间和最大值。

②如果为负数,取区间和最小值。

可以用线段树来维护前缀和。然后查找区间最值。

如果为正数,那么直接右端点前缀和-左端点前缀和

如果为负数,这时你要找区间和最小值,那么你可以右端找一个最小值-左端找一个最大值,那么这时就是最小值(如果左端最大值为负值,这时负数-负数会变大,所以不能减)。

代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500000+50;
const long long INF = 0x3f3f3f3f;
typedef long long ll;
ll a[maxn], pre[maxn];
ll minn[maxn<<2], maxx[maxn<<2];
int Left[maxn], Right[maxn];

void buildTree(int l, int r, int rt)
{
	if(l == r) {
		maxx[rt] = minn[rt] = pre[l];
		return;
	}
	int mid = (l+r)>>1;
	buildTree(l,mid,rt<<1);
	buildTree(mid+1,r,rt<<1|1); 
	maxx[rt] = max(maxx[rt<<1], maxx[rt<<1|1]);
	minn[rt] = min(minn[rt<<1], minn[rt<<1|1]);
}
int qmax(int l, int r, int rt, int L, int R)
{
	if(L <= l && r <= R)
	{
		return maxx[rt];
	}
	int mid = (l + r) >> 1;
	int ans = -INF;
	if(L <= mid)
		ans = max(ans, qmax(l, mid, rt<<1, L, R));
	if(R > mid)
		ans = max(ans, qmax(mid+1, r, rt<<1|1, L, R));
	return ans;
}
int qmin(int l, int r, int rt, int L, int R)
{
	if(L <= l && r <= R)
	{
		return minn[rt];
	}
	int mid = (l + r) >> 1;
	int ans = INF;
	if(L <= mid)
		ans = min(ans, qmin(l, mid, rt<<1, L, R));
	if(R > mid)
		ans = min(ans, qmin(mid+1, r, rt<<1|1, L, R));
	return ans;
}
int main()
{
	int t;
	scanf("%d", &t);
	int x; 
	for(int i = 1; i <= t; i++) {
		scanf("%lld", &a[i]);
		pre[i] = pre[i-1] + a[i];
	}
	buildTree(1, t, 1);
	stack<int > s;
	while(s.size()){
		s.pop();
	}
	for(int i = 1; i <= t; i++) {
		while(s.size() && a[s.top()]>a[i]) {
			Right[s.top()] = i-1;
			s.pop();
		}
		s.push(i);
	}	
	while(s.size()) {
		Right[s.top()] = t;
		s.pop();
	}
	for(int i = t; i >= 1; i--) {
		while(s.size() && a[s.top()] > a[i]) {
			Left[s.top()] = i+1;
			s.pop();
		}
		s.push(i);
	}
	while(s.size()) {
		Left[s.top()] = 1;
		s.pop();
	}
	ll res = -1e18;
	for(int i = 1; i <= t; i++) {
		if(a[i] > 0) {
			res = max(res, a[i] * (pre[Right[i]] - pre[Left[i] - 1]));
		}
		else {
			res = max(res, a[i] * (qmin(1,t,1,i,Right[i]) - max(0,qmax(1, t, 1, Left[i], i))));
		}
	}
	printf("%lld\n", res);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值