1. 程式人生 > >【洛谷】【堆+結論】P4597 序列sequence

【洛谷】【堆+結論】P4597 序列sequence

由於 最大值 隊列 get alt 選擇 向上 span 小數

【題目背景:】

原題cf13c 數據加強版(就是說原來能用DP做現在不行了QwQ)

【題目描述:】

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

【輸入格式:】

第一行輸入一個n,表示有n( n \leq 5*10^5n5?105 )個數字。第二行輸入n個整數,整數的絕對值不超過 10^9109

【輸出格式:】

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

【算法分析:】

切題背景:
chen_zhe大佬改編了這道題目之後吸引了slyz準高一全機房同學的註意

主要是一開始是道紫題想要通過以後強行把它變黑(霧

然後全機房的同學剛了一節課4597無果(中間它突然變成了黑題QwQ)

然後就被隔壁機房的學長學姐拯救了...

自己想的證明:

對於一個序列,可以看成高度不同的幾根線

如:序列{3、 4、 1、 5}可以看做這樣

技術分享圖片
                  ----                
     ----
---- 
          
            ----
3 4 1 5

對於一個大數a和一個小數b,要做的就是在他們之間的任意位置找到一個基準,將大數向下挪到那個基準,小數向上挪到那個基準

移動的距離等價於a - b

由於是非降序列,將a向下移動的距離越多越可以使之後的數字更容易變成非降序列

所以這個基準應該是選擇之前的最小數c,而之前的最小數一定是在[a, b]這個區間內,將a向下移到c並將b向上移到c的距離等價於將a向下移動到b的距離

所以就把a移到b就好了

然後開個大根堆瞎搞:

對於讀進的一個數num,把它push到優先隊列裏去

如果這個num比之前的最大值maxn(就是堆頂元素)要小的話

  ans += maxn - num

  並把maxn彈出,再push進一個num(把maxn移動到了num的位置)

【代碼:】

 1 //序列sequence加強版
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
7 8 int n; 9 long long ans; 10 priority_queue<int> q; 11 12 inline int read() { 13 int x = 0, f = 1; char ch = getchar(); 14 while(ch < 0 || ch > 9) { if(ch == -) f = -1; ch = getchar(); } 15 while(ch >= 0 && ch <= 9) 16 x = (x << 3) + (x << 1) + ch - 48, ch = getchar(); 17 return x * f; 18 } 19 20 int main() { 21 n = read(); 22 for(int i = 1; i <= n; i++) { 23 int num = read(); 24 q.push(num); 25 if(num < q.top()) { 26 ans += q.top() - num; 27 q.pop(); 28 q.push(num); 29 } 30 } 31 printf("%lld\n", ans); 32 }

【洛谷】【堆+結論】P4597 序列sequence