[题解]P4597 序列 sequence

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\)不一定连续,后面就懂了),然后对它们的值进行上调下调。

如上图,我们发现\(10\to 1\)不满足“不降”,需要进行调整。我们想到在\([1,10]\)中找到一个中间点,把这\(2\)个数都移动到中间点。而无论中间点是几,这一操作代价都为\(10-1=9\)。那么我们为了让操作次数尽可能小,我们应该尽可能让中间点往下,即移动到\(1\)

总结:如果遇到\(y\)前面的最大值\(x\),满足\(x>y\),我们需要把\(y\)下移到\(x\)的位置。从左到右遍历每条边,重复上述操作即可。

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

  • 如果\(z\)后面又遇到一个值\(a_i<a_{i-1}\),而它之前的最大值正是\(z\),它就会把\(z\)压下去。
    但如下图,我们又发现,\(z\)下调后,后边又不满足不降,此时就把\(x\)作为\(z\)循环上面的步骤。

  • 如果\(z\)后面没有更低的值,则说明\(y\)没必要降到\(x\),中间点可以上移到\(z\)也不会影响结果,但在此同时我们让原序列合法了。而中间点只要在\([x,y]\)中间,消耗就相同,所以答案不受影响。

综上,答案不会受下移的影响,尽管序列可能不满足“不降”,但我们可以在消耗不变的情况下调整至满足条件。


接下来就是代码实现。为了维护前面的最大值,我们开一个优先队列。
对于输入的每一个\(x\)
首先加入\(x\)
如果遇到\(x<max\),则\(ans+=max-x\),然后\(max\)出队列,加入下调后的高度\(x\)

时间复杂度\(O(N\log N)\)

虽然加强版空间限制从64MB提升到了125MB,但是这份代码可以过CF的原题,因为空间消耗只有\(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;
}
posted @ 2024-05-01 17:32  Sinktank  阅读(30)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.