AcWing3780 构造数组 (思维 单调栈)

题目链接: 构造数组

大致题意

如题面所述

解题思路

思维

首先我们分析题目中的条件②, 这个条件表明我们最终的序列应当是不存在波谷的, 表明整个序列最多可以存在一个波峰. 若不存在波峰, 则整个序列呈单调递增(减).(也可以认为 第一个/最后一个 位置是波峰)

我们可以枚举最大值(波峰)出现的位置. 不妨设最大值位置为pos, 则a[pos] = mpos. 对于[1, pos - 1]区间, 我们可以从右往左递推出ai的值, 使得[1, pos]非递减. 而对于[pos + 1, n]区间, 我们同理可以从左往右递推出ai的值.

考虑到这样的操作复杂度会是O(n2)的. 接下来考虑优化


单调栈

考虑到每次枚举波峰, 波峰左侧的区间[1, pos), 我们进行从右向左扫描. 我们发现如果mi >= mpos的位置, 只能取成mpos, 当出现mi < mpos时, 此时ai = mi (设第一个满足该条件的位置为index). 这表明: 当pos位置为波峰时, 其产生的左侧贡献为: index位置为波峰时的贡献 + (pos - index) * m[pos].

这里可以借用主席树的思维, 相当于是pos版本的贡献 = index版本的贡献 + [index + 1, pos]区间的贡献.

我们发现, 当我们枚举不同位置为波峰时, 其左区间的贡献可以由第一个比该位置小的index位置得到.

而找到第一个比某个位置小的位置, 我们可以用单调栈进行维护.

对于波峰右侧的情况同理.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10;
int a[N];
ll l[N], r[N]; // 记录左右区间的贡献
int main()
{
	int n; cin >> n;
	rep(i, n) scanf("%d", &a[i]);

	stack<int> st; st.push(0); //设置哨兵 0位置比任何位置都小
	rep(i, n) {
		while (a[st.top()] >= a[i]) st.pop(); //不用判空, 因为0位置始终会在栈内
		l[i] = l[st.top()] + 1ll * (i - st.top()) * a[i];
		st.push(i);
	}

	st = stack<int>(); //清空栈
	st.push(n + 1);
	for (int i = n; i >= 1; --i) {
		while (a[st.top()] >= a[i]) st.pop();
		r[i] = r[st.top()] + 1ll * (st.top() - i) * a[i];
		st.push(i);
	}

	ll res = 0; int pos = 0;
	rep(i, n) {
		ll now = l[i] + r[i + 1];
		if (now > res) res = now, pos = i;
	}

	for (int i = pos - 1; i >= 1; --i) a[i] = min(a[i], a[i + 1]); // 模拟过程
	for (int i = pos + 2; i <= n; ++i) a[i] = min(a[i], a[i - 1]); // 模拟过程

	rep(i, n) printf("%d%c", a[i], " \n"[i == n]);

	return 0;
}

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值