凸壳优化dp/spole trick

写给自己的一点复习与总结

(主要是今天高强度盯着东西罚坐以及晚上和d*h battle了很久,所以来写点东西)

(话说,最近用onenote记东西习惯了,一下子有点不适应)


spole trick

当一些dp直接进行复杂度不可接受的时候,如果这个dp的斜率都是单调递增(或递减的),并且都是整数,那么我们可以考虑这么优化

如果我们将某个函数 g ( x ) g(x) g(x) 的所有斜率改变1的点维护下来,(如果在某个点 x x x 斜率的变化大于1,那么这个位置要记录多次),那么我们就维护出来了这个函数

那么问题是? 我们怎么求想要的函数值呢?

对于最小值dp问题来说,斜率最接近0的部分,应该就是所求答案。

对于函数图像,我们可以这么理解,斜率为0的一段上的dp值都相同(但我们此时还不知道是多少),因此,在所有能给这一段上的点更新答案的位置,使得更新出来的答案最小的,就是我们想要找的点(同时,因为要使得答案在最小范围内,用于更新答案的位置应该在这段上)

例1

CF713C Sonya and Problem Wihtout a Legend

首先先把每个位置的值减去 i i i,就转单调递增单调不降

我们定义 f i , j f_{i,j} fi,j 为确定到第 i i i 个数,且第 i i i 个数小于等于 j j j

我们定义 g i , j g_{i,j} gi,j 为确定到第 i i i 个数,且第 i i i 个数等于 j j j

这样的定义上 f i , j = min ⁡ k < = j g i , k f_{i,j}=\min\limits_{k<=j}g_{i,k} fi,j=k<=jmingi,k

g i , j = f i , j + ∣ j − a i ∣ g_{i,j}=f{i,j}+|j-a_i| gi,j=fi,j+jai

两个函数同时具有凹性,所以考虑用这个技巧维护

但问题来了?我们怎么统计答案呢?

贴个代码

#include<bits/stdc++.h>
using namespace std;
int n;
long long ans;
priority_queue<int> q;
int main(){
	int x; scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&x); x-=i;
		q.push(x); q.push(x);
		ans+=q.top()-x; q.pop();
	}
	printf("%lld",ans);
	return 0;
}

??为什么这样就是对的

考虑一下,我们每次都是要在 f i − 1 f_{i-1} fi1 的基础上得到 f i , g i f_{i},g{i} fi,gi

因为 f f f 是前缀最小值,所以f图像的最后一段必然是一段斜率为0到无限远处的射线

当我们加入新的点之后,其实已经完成了,从 f i − 1 f_{i-1} fi1 g i g_{i} gi 的过程

但,为什么是在这个时候再计算答案?

考虑一下, i − 1 i-1 i1次时我们取得答案的位置,一定是在 f i − 1 f_{i-1} fi1斜率欲往上增加的拐点得到的,这个拐点左侧,函数值变大,右侧是直线

那么此时我们加入一个点 a i a_i ai ,如果 a i a_i ai 拐点左侧,那么因为 f i − 1 f_{i-1} fi1 是一条后面平滑的直线,所以没有影响

但要是小于拐点?为什么取拐点做差就是对的?

思考一下,因为斜率最大改变1,当这个点加入后,拐点的位置会成为从斜率0变为斜率1的位置
在这里插入图片描述
在这里插入图片描述

(其中彩色点是同一个点,也就是拐点)

因为拐点仍然在斜率为0的直线上,所以在这里取得的答案仍然是最小答案,也因为 f i − 1 f_{i-1} fi1是在这里取得的,所以此时不能选择更左边的点去计算答案(题目要求,选择的点单调不降)

但是选择完点之后, f i f_i fi 的斜率为0的拐点提前了,含义也就是:当 j j j 为这一段上面的点时,所取得的答案都是同样小的,因此在向 i + 1 i+1 i+1转移时,就可以选择更左边的点去转移了(也就是删除了最左边的点,从 g g g f f f 的过程)

因此,不断维护堆且取堆顶是对的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值