[題解]P4597 序列 sequence

Sinktank發表於2024-05-01

P4597 序列 sequence
CF13C Sequence的加強版,\(N\leq 5*10^5\)

如果想了解\(O(N^2)\)的DP解法請看此文


給定\(N\)個數,每次操作可以選其中一個數\(+1\)\(-1\)。請問要讓這個數列不降,最少需要多少次操作?

看到資料範圍發現不能用\(O(N^2)\)的dp了,需要換一種思路。

我們用類似貪心的方法做,從左往右找\(i<j,a_i>a_j\)(注意\(i,j\)不一定連續,後面就懂了),然後對它們的值進行上調下調。

[題解]P4597 序列 sequence

如上圖,我們發現\(10\to 1\)不滿足“不降”,需要進行調整。我們想到在\([1,10]\)中找到一箇中間點,把這\(2\)個數都移動到中間點。而無論中間點是幾,這一操作代價都為\(10-1=9\)。那麼我們為了讓操作次數儘可能小,我們應該儘可能讓中間點往下,即移動到\(1\)

總結:如果遇到\(x\)\(y\)後面,而\(x>y\),我們需要把\(y\)下移到\(x\)的位置。從左到右遍歷每條邊,重複上述操作即可。

大家可能會有疑惑:如果\(y\)前面的\(z\),滿足\(x<z<y\),如果把\(y\)下移到\(x\),就會破壞前面的“不降”:

[題解]P4597 序列 sequence
  • 如果\(z\)後面又遇到一個值\(a_i<a_{i-1}\),而它之前的最小值正是\(z\),它就會把\(z\)壓下去。
    但如下圖,我們又發現,\(z\)下調後,後邊又不滿足不降,此時就把\(x\)作為\(z\)迴圈上面的步驟。
    [題解]P4597 序列 sequence

  • 如果\(z\)後面沒有更低的值,則說明\(y\)沒必要降到\(x\),中間點可以上移到\(z\)也不會影響結果,但在此同時我們讓原序列合法了。而中間點只要在\([x,y]\)中間,消耗就相同,所以答案不受影響。
    [題解]P4597 序列 sequence

綜上,答案不會受下移的影響,儘管序列可能不滿足“不降”,但我們可以在消耗不變的情況下調整至滿足條件。


接下來就是程式碼實現。為了維護前面的最大值,我們開一個優先佇列。
對於輸入的每一個\(x\)
首先加入\(x\)
如果遇到\(x<max\),則\(ans+=max-x\),然後\(max\)出佇列,加入下調後的高度\(x\)

時間複雜度\(O(N\log N)\)

雖然加強版空間限制從64MB提升到了125MB,但是這份程式碼可以過CF的原題,因為空間消耗只有\(\log N\)而已。

注意開long long

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
using namespace std;
priority_queue<int> q;
int n,x,ans;
signed main(){
	cin>>n;
	while(n--){
		cin>>x;
		q.push(x);
		if(x<q.top()){
			ans+=q.top()-x;
			q.pop();
			q.push(x);
		}
	}
	cout<<ans;
	return 0;
}

相關文章