Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version)-单调栈+dp

题目链接: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]1i1ib[i]f[1[[i]:a[i]niinb[i]f[0][i]+f[1][i]a[i]i1nb[i]if(a[i]>a[i1])f[0][i]=f[0][i1]+a[i];elsef[0][i]=f[0][l[i]]+(il[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值