牛客多校第四场——sequence(单调栈+线段树)

题目链接:https://ac.nowcoder.com/acm/contest/884/C

链接:https://ac.nowcoder.com/acm/contest/884/C
来源:牛客网
 

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

Your are given two sequences a1…na_{1 \dots n}a1…n​ and b1…nb_{1 \dots n}b1…n​ .You need to answer max⁡1≤l≤r≤n{min(al…r)×sum(bl…r)}\displaystyle \max_{1 \le l \le r \le n} \{min(a_{l \dots r}) \times sum(b_{l \dots r})\}1≤l≤r≤nmax​{min(al…r​)×sum(bl…r​)} 。

Where min(a) means the minimal value of every element of sequence a, sum(a) means the sum of every element of sequence a .

输入描述:

The first line contains an integer n .

The second line contains n integers meaning a1…na_{1 \dots n}a1…n​ .

The third line contains n integers meaning b1…nb_{1 \dots n}b1…n​ .

输出描述:

An integer meaning the answer.

示例1

输入

复制

3
1 -1 1
1 2 3

输出

复制

3

备注:

For all test datas, 1≤n≤3×106,−106≤ai,bi≤1061 \leq n \leq 3 \times 10^6,-10^6 \leq a_i,b_i \leq 10^61≤n≤3×106,−106≤ai​,bi​≤106.

思路:

对于a数组中的每一个数a[i],它作为最小值的作用空间为左边第一个小于他的数,到右边第一个小于他的数

所以可以用单调栈维护左右两边第一个小于他的数

对于第二个序列,用线段数维护区间前缀和的最大最小值,当一个区间的a[i]一定时,就可以在b[i]的区间前缀和中寻找最大值最小值,如果a[i]大于0,则在i到右边第一个小于它的数 之间寻找最大的前缀和,在左边第一个小于他的数到i寻找最小的前缀和,两者相减;如果a[i]小于0,则情况相反,用最小值减去最大值即可。

(比赛可以过,测评机不稳定赛后就T了)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
#include <stack>
//#define int long long 
using namespace std;
typedef long long ll;
const int maxn = 3e6 + 10;
const ll INF = 0x3f3f3f3f3f3f;
int a[maxn];
int b[maxn];
struct Node {
	int l, r;
	ll maxx;
	ll minn;
}Tree[maxn<<2];
void Build(int root, int l, int r) {
	Tree[root].l = l, Tree[root].r = r;
	Tree[root].maxx = Tree[root].minn = 0;
	if (l == r)return;
	int mid = (l + r) >> 1;
	Build(root << 1, l, mid);
	Build(root << 1 | 1, mid + 1, r);
}
void pushup(int root) {
	Tree[root].maxx = max(Tree[root << 1].maxx, Tree[root << 1 | 1].maxx);
	Tree[root].minn = min(Tree[root << 1].minn, Tree[root << 1 | 1].minn);
}
void Insert(int root, int pos, ll val) {
	if (Tree[root].l == Tree[root].r) {
		Tree[root].maxx = val;
		Tree[root].minn = val;
		return;
	}
	int mid = (Tree[root].l + Tree[root].r) >> 1;
	if (pos <= mid) {
		Insert(root << 1, pos, val);
	}
	else {
		Insert(root << 1 | 1, pos, val);
	}
	pushup(root);
}
ll query(int root, int l, int r, int op) {
	if (Tree[root].l >= l && Tree[root].r <= r) {
		return op == 0 ? Tree[root].maxx : Tree[root].minn;
	}
	int mid = (Tree[root].l + Tree[root].r) >> 1;
	ll maxx = -INF, minn = INF;
	if (l <= mid) {
		if (op == 0) {
			maxx = max(maxx, query(root << 1, l, r, op));
		}
		else {
			minn = min(minn, query(root << 1, l, r, op));
		}
	}
	if (r > mid) {
		if (op == 0) {
			maxx = max(maxx, query(root << 1 | 1, l, r, op));
		}
		else {
			minn = min(minn, query(root << 1 | 1, l, r, op));
		}
	}
	return op == 0 ? maxx : minn;
}
int st[maxn][2];
ll sum[maxn];
int n;
queue<int>q;
stack<int>s;
signed main() {
	ios::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
	cin >> n;
	for (int i = 1; i <= n; ++i)cin >> a[i];
	sum[0] = 0;
	for (int i = 1; i <= n; ++i) { cin >> b[i]; sum[i] = sum[i - 1] + b[i]; }
	for (int i = 1; i <= n; ++i)
	{
		while (!s.empty() && a[s.top()] > a[i]) {
			int inx = a[s.top()];
			while (!s.empty() && a[s.top()] == inx) {
				//cerr << "debug " << endl;
				q.push(s.top());
				s.pop();
			}
			while (!q.empty()) {
				st[q.front()][1] = i;
				//cerr << "emmm   "<<i << endl;
				q.pop();
			}
		}
		st[i][0] = s.empty() ? 0 : s.top();
		s.push(i);
	}
	while (!s.empty()) {
		st[s.top()][1] = n + 1;
		s.pop();
	}
	Build(1, 0, n);
	for (int i = 0; i <= n; i++) {
		Insert(1, i, sum[i]);
	}
	ll ans = -INF;
	for (int i = 1; i <= n; i++) {
		if (a[i] >= 0) {
			ans = max(ans, (query(1, i, st[i][1] - 1, 0) - query(1, st[i][0], i, 1))*a[i]);
		}
		else {
			ans = max(ans, (query(1, i, st[i][1] - 1, 1) - query(1, st[i][0], i, 0))*a[i]);
		}
	}
	cout << ans << endl;
	//system("pause");
	return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值