专栏文章

题解:P12241 [蓝桥杯 2023 国 C] 最大区间

P12241题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipjnk8k
此快照首次捕获于
2025/12/03 13:05
3 个月前
此快照最后确认于
2025/12/03 13:05
3 个月前
查看原文

题意简述

给定一个长度为 nn 的序列,求最大的 (RL+1)×min(AL,AL+1,,AR)(R - L + 1) \times \min(A_L, A_{L+1}, \ldots, A_R) 的值,就是区间长度乘以区间最小值的最大值。

算法思路

通过正向逆向两次单调栈扫描预处理,找出每个点作为区间最小点能获得的最大区间,很明显,区间越长越好。有一道极其相似的题:洛谷 P2422 良好的感觉
  1. 确定左右边界
    • 先初始化,初始化左端点为 00,右端点为 n+1n+1
    • 正向逆向跑一遍单调栈,求出每个点左边和右边第一个比 AiA_i 小的点的位置。
    • 此时 AiA_i 作为最小值的最大区间为 (li,ri)(l_i, r_i),实际正确的区间长度为 rili1r_i - l_i - 1,因为是以 AiA_i 为最小点的区间。
  2. 计算时注意: 区间不包含左右端点。

复杂度分析

  • 时间复杂度O(n)O(n) 正向逆向各一次扫描。

代码实现

CPP
#include<cstdio>
#include<stack>
#include<climits>
#include<algorithm>

using namespace std;

typedef long long ll;

const ll maxn = 3e5 + 5;

ll n, num[maxn];
void Input() {
	scanf("%lld", &n);
	for (ll i = 1; i <= n; ++i) {
		scanf("%lld", &num[i]);
	}
}


ll l[maxn], r[maxn];
void Init() {
	for (ll i = 1; i <= n; ++i) {
		l[i] = 0;
		r[i] = n + 1;
	}
}

stack<ll> s;
ll ans = INT_MIN;
void Sol() {
	for (ll i = 1; i <= n; ++i) {
		while (!s.empty() && num[s.top()] > num[i]) {
			r[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}
	while (!s.empty()) {
		s.pop();
	}
	for (ll i = n; i >= 1; --i) {
		while (!s.empty() && num[s.top()] > num[i]) {
			l[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}
	for (ll i = 1; i <= n; ++i) {
		ans = max(ans, ((r[i] - 1) - (l[i] + 1) + 1) * num[i]);
	}
}

int main() {
	Input();
	Init();
	Sol();
	printf("%lld", ans);
	return 0;
}
注意:初始化时左右边界分别为 00n+1n+1

评论

0 条评论,欢迎与作者交流。

正在加载评论...