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