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]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) \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}

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章