用优先队列替代平衡树

用优先队列替代平衡树

例题:[P4272 CTSC2009]序列变换 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

考虑这样一个问题:

初始\(\forall _{i\in Z},f_i=0\)

转移:

\(1. f_i+=abs(i-T)\)

\(2. f_{i}=\min\{f_j\},(j\in [i-l,i+r])\)

\(\min \{f_{i}\}\)

首先\(f\)构成的图像一定是一段一段的,且是下凸的\((斜率递增)\)

把图像画出来可以发现:

  1. 对应了在\(x=T\)处插入一条斜率为\(±1\)\(V\)形折线。
  2. 对应了将图像右移\(l\),左移\(r\)然后取一个\(\min\)。将斜率为\(0\)的某一端看作中点\(mid\),由于下凸的性质,所以可以看作将\(mid\)左边的部分左移了\(r\)\(mid\)右边的部分右移了\(l\)

所以这个问题就变成了:

给你一堆线段,斜率递增。

  1. \(>T\)的部分的斜率\(+1\),将\(<T\)的部分的斜率\(-1\)
  2. 将斜率\(>0\)的部分右移\(A\),将斜率\(\leq 0\)的部分左移1。

这个非常显然可以用平衡树来实现。

不过实现起来非常繁琐,且常数较大。

这种问题其实也可以用优先队列非常方便地实现,且常数非常小。

实现方法:

用两个\(\text{priority_queue<int>}\) \(L,R\)分别存储\(mid\)左边斜率的变换点,\(mid\)右边斜率的变化点,斜率每次变化1 。

考虑平移:

直接打tag:\(tagl,tagr\)

加入端点在\(T\)\(V\)字形,在\(L\)\(R\)中插入\(T\),然后调整:每次取出堆顶然后交换。

code(序列变换):

int n,q,a,b;
LL num[500000+20];
LL tagl,tagr;
LL tmp[500000+20];
int main(){
	cin>>n>>q>>a>>b;
	LL rest=0;
	rb(i,1,n) scanf("%lld",&num[i]),rest+=num[i];
	priority_queue<LL> L;
	priority_queue<LL,vector<LL>,greater<LL> > R;
	L.push(num[1]);
	R.push(num[1]);
	rb(i,2,n){
		tagl+=a;
		tagr+=b;
		rest+=1ll*a*(i-1);
		if(num[i]<tagl+1){
			rest+=2ll*(tagl+1-num[i]);
			num[i]=tagl+1;
		}
		L.push(num[i]-tagl);
		R.push(num[i]-tagr);
		do{
			LL l,r;
			l=L.top()+tagl;
			r=R.top()+tagr;
			if(l<=r) break;
			L.pop(),R.pop();
			L.push(r-tagl);
			R.push(l-tagr);
		}while(true); 
	}
	rb(i,0,n-1) tmp[i]=L.top()+tagl,L.pop(),check_min(tmp[i],1ll*q);
	rl(i,n-1,0) rest-=(tmp[i]-tmp[i+1])*(i+1);
	cout<<rest<<endl;
	return 0;
}
posted @ 2021-05-07 12:47  WWW~~~  阅读(73)  评论(0编辑  收藏  举报