题目链接: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单增时。∑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;
}