题目链接: 构造数组
大致题意
如题面所述
解题思路
思维
首先我们分析题目中的条件②, 这个条件表明我们最终的序列应当是不存在波谷的, 表明整个序列最多可以存在一个波峰. 若不存在波峰, 则整个序列呈单调递增(减).(也可以认为 第一个/最后一个 位置是波峰)
我们可以枚举最大值(波峰)出现的位置. 不妨设最大值位置为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;
}