用优先队列替代平衡树
用优先队列替代平衡树
例题:[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\)构成的图像一定是一段一段的,且是下凸的\((斜率递增)\)
把图像画出来可以发现:
- 对应了在\(x=T\)处插入一条斜率为\(±1\)的\(V\)形折线。
- 对应了将图像右移\(l\),左移\(r\)然后取一个\(\min\)。将斜率为\(0\)的某一端看作中点\(mid\),由于下凸的性质,所以可以看作将\(mid\)左边的部分左移了\(r\),\(mid\)右边的部分右移了\(l\)。
所以这个问题就变成了:
给你一堆线段,斜率递增。
- 将\(>T\)的部分的斜率\(+1\),将\(<T\)的部分的斜率\(-1\)。
- 将斜率\(>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;
}