题目链接:https://codeforces.ml/contest/1313/problem/C2
题目大意:
有n栋房子。每个房子的最高的高度b[i]应该在[1, a[i]]区间里。现在要满足不存在i<j<k有a[i]>a[j]<a[k]。并且要b[i]的和最大。输出b[i]-b[n]。方案可能不唯一。随便输出一个就可以。
思路:我们可以知道b[i]数组是一个单峰函数。并且最高的b[i]一定等于a[i]。
f
[
0
[
[
i
]
:
a
[
i
]
为
1
−
i
单
增
时
。
∑
1
i
b
[
i
]
的
最
大
值
f
[
1
[
[
i
]
:
a
[
i
]
为
n
−
i
单
增
时
。
∑
i
n
b
[
i
]
的
最
大
值
那
么
f
[
0
]
[
i
]
+
f
[
1
]
[
i
]
−
a
[
i
]
就
是
以
第
i
处
为
最
高
点
时
∑
1
n
b
[
i
]
的
最
大
值
我
们
考
虑
状
态
转
移
方
程
:
i
f
(
a
[
i
]
>
a
[
i
−
1
]
)
f
[
0
]
[
i
]
=
f
[
0
]
[
i
−
1
]
+
a
[
i
]
;
e
l
s
e
f
[
0
]
[
i
]
=
f
[
0
]
[
l
[
i
]
]
+
(
i
−
l
[
i
]
)
∗
a
[
i
]
;
f
[
1
]
[
i
]
的
状
态
转
移
方
程
类
似
。
l
[
i
]
:
左
边
第
一
个
<
=
a
[
i
]
的
坐
标
。
r
[
i
]
:
右
边
第
一
个
<
=
a
[
i
]
的
坐
标
l
[
i
]
和
r
[
i
]
可
以
用
单
调
栈
O
(
N
)
。
分
块
O
(
N
)
。
线
段
树
O
(
log
n
)
。
s
e
t
O
(
log
n
)
求
。
\begin{array}{l} f[0[[i]:a[i]为1-i单增时。\sum_{1}^{i}b[i]的最大值\\ f[1[[i]:a[i]为n-i单增时。\sum_{i}^{n}b[i]的最大值\\ 那么f[0][i]+f[1][i]-a[i]就是以第i处为最高点时\sum_{1}^{n}b[i]的最大值\\\\ 我们考虑状态转移方程:\\ if(a[i]>a[i-1]) \\f[0][i]=f[0][i-1]+a[i]; \\\\ else\\ f[0][i]=f[0][l[i]]+(i-l[i])*a[i];\\\\ f[1][i]的状态转移方程类似。\\ l[i]:左边第一个<=a[i]的坐标。r[i]:右边第一个<=a[i]的坐标\\ l[i]和r[i]可以用单调栈O(N)。分块O(\sqrt{N})。线段树O(\log{n})。setO(\log{n})求。 \end{array}
f[0[[i]:a[i]为1−i单增时。∑1ib[i]的最大值f[1[[i]:a[i]为n−i单增时。∑inb[i]的最大值那么f[0][i]+f[1][i]−a[i]就是以第i处为最高点时∑1nb[i]的最大值我们考虑状态转移方程:if(a[i]>a[i−1])f[0][i]=f[0][i−1]+a[i];elsef[0][i]=f[0][l[i]]+(i−l[i])∗a[i];f[1][i]的状态转移方程类似。l[i]:左边第一个<=a[i]的坐标。r[i]:右边第一个<=a[i]的坐标l[i]和r[i]可以用单调栈O(N)。分块O(N)。线段树O(logn)。setO(logn)求。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[500005], l[500005], r[500005];
LL f[2][500005], tot=0, g[500005];
struct node{
LL x, i;
}q[500005];
int main(){
int n;scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%lld", &a[i]);
}
//单调栈
for(int i=1; i<=n; i++){
if(tot==0||q[tot].x<a[i]){
q[++tot]={a[i], i};
}
while(tot!=0&&q[tot].x>=a[i]){
l[q[tot].i]=q[tot-1].i;
r[q[tot].i]=i;
tot--;
}
q[++tot]={a[i], i};
}
while(tot){
l[q[tot].i]=q[tot-1].i;
r[q[tot].i]=n+1;
tot--;
}
//dp
for(int i=1; i<=n; i++){
if(a[i]>a[i-1]){
f[0][i]=f[0][i-1]+a[i];
}
else{
f[0][i]=f[0][l[i]]+(i-l[i])*a[i];
}
}
for(int i=n; i>=1; i--){
if(a[i]>a[i+1]){
f[1][i]=f[1][i+1]+a[i];
}
else{
f[1][i]=f[1][r[i]]+(r[i]-i)*a[i];
}
}
//方案
LL mx=0, pos=0, now=0;
for(int i=1; i<=n; i++){
if(f[0][i]+f[1][i]-a[i]>mx){
mx=f[0][i]+f[1][i]-a[i];
pos=i;
}
}
g[pos]=a[pos], now=a[pos];
for(int i=pos+1; i<=n; i++){
if(a[i]>=now){
g[i]=now;
}
else{
g[i]=a[i];
now=g[i];
}
}
now=a[pos];
for(int i=pos-1; i>=1; i--){
if(a[i]>=now){
g[i]=now;
}
else{
g[i]=a[i];
now=g[i];
}
}
for(int i=1; i<=n; i++){
printf("%lld ", g[i]);
}
return 0;
}