【洛谷】【堆+結論】P4597 序列sequence
阿新 • • 發佈:2018-05-14
由於 最大值 隊列 get alt 選擇 向上 span 小數
【題目背景:】
原題cf13c 數據加強版(就是說原來能用DP做現在不行了QwQ)
【題目描述:】
給定一個序列,每次操作可以把某個數+1-1。要求把序列變成非降數列。而且要求修改後的數列只能出現修改前的數。
【輸入格式:】
第一行輸入一個n,表示有n( n \leq 5*10^5n≤5?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