1. 程式人生 > >洛谷 P4597 序列sequence 解題報告

洛谷 P4597 序列sequence 解題報告

數字 現在 clas %d 自己 1.7 報告 sca 理解

P4597 序列sequence

題目背景

原題\(\tt{cf13c}\)數據加強版

題目描述

給定一個序列,每次操作可以把某個數\(+1\)\(-1\)。要求把序列變成非降數列。而且要求修改後的數列只能出現修改前的數。

輸入輸出格式

輸入格式:

第一行輸入一個\(n\),表示有\(n(n \leq 5\times10^5)\)個數字。

第二行輸入\(n\)個整數,整數的絕對值不超過\(10^9\)

輸出格式:

輸出一個數,表示最少的操作次數


發現之前洛谷做過一個類似的。。P2893

chen_zhe的題解並沒有看懂。

原題的\(N^2\)思路比較好想,離散化後直接開到狀態裏面就可以了。

然後維護一個按時間順序維護一個完整的非降數列,假設當前維護到位置\(i\)

當前數列的末尾為\(p\)

  • \(p\le a_i\)

    直接加入數列

  • \(p>a_i\)

    則把\(p\)\(a_i\)作為一個二元組\((a_i,p)\)拿出來,那麽一定要花\(a_i-p\)的代價讓這個二元組變得不降

    如果不考慮其他情況,那麽這個二元組可以取到的值為\((a_i,a_i),(a_i+1,a_i+1),\dots ,(p,p)\)

    顯然取到最小值最好,那麽我們就當\(\tt{Ta}\)取到了最小值,然後把\(\tt{Ta}\)的最小值放到數列。雖然這時候可能比現在的末尾要小,不過沒關系,現在的新末尾也可能會改變。我們就當這個二元組在末尾大於\(\tt{Ta}\)

    的最小取值的時候,把\(\tt{Ta}\)當末尾那麽大就可以了。

可以簡單的拿一個大根堆維護上述過程。

考慮為什麽這樣一定可以取到修改前的數,其實也很好理解,畢竟我們二元組取值要麽是自己,要麽就是某個末尾啊。


Code:

#include <cstdio>
#include <queue>
#define ll long long
std::priority_queue <ll> q;
ll ans=0,a;int n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a);
        if(!q.empty()&&q.top()>a)
        {
            ans+=q.top()-a;
            q.pop();
            q.push(a);
        }
        q.push(a);
    }
    printf("%lld\n",ans);
    return 0;
}

2018.11.7

洛谷 P4597 序列sequence 解題報告