slope trick 学习笔记
首先,请注意 \(Slope Trick\) 可不是斜率优化,是一种通过维护斜率序列的优化,它是有一定范围限制的:
- 连续。
- 是分段一次函数。
- 是凸函数。
- 每一段的斜率较小(通常为 \(O(n)\)),且均为整数。
我们发现如果此上类型的函数相加之后还是上面的一种函数。
它通常用来维护 \(DP\),维护满足以上条件的函数的斜率加减来将函数平移,翻转,相加, 取前缀/后缀 \(min\), 求 \(min,argmin\), 维护方法如下:
可以先维护在某个 \(x_0\) 处的 \(f(x_0)\),\(k_0\) 以及函数每个斜率变化点的集合。具体地,如果函数在 \(x\) 位置斜率增加了 \(\Delta_k\),就在集合中插入 \(\Delta_k\) 个 \(x\)。
对于相加:将 \(f(x_0)\),\(k_0\) 直接相加,斜率变化点的集合直接合并。常用于加一次函数、绝对值函数。
对于取前缀/后缀 \(min\):去掉 \(k >0\ or\ k < 0\) 的部分。
对于求 \(min,argmin\): 提取 \(k = 0\) 的部分
对于平移:维护 \(f(x_0)\),\(k_0\) 的变化,斜率变化点在全局打平移标记。
对于翻转:维护 \(f(x_0)\),\(k_0\) 的变化,斜率变化点在全局打翻转标记。
例题
# P4597 序列 sequence 以及 # CF13C Sequence,# CF713C Sonya and Problem Wihtout a Legend,# P2893 [USACO08FEB] Making the Grade G 其实是一道题啦。
先让我去打一下,还没打QwQ
嗨嗨嗨,打完啦,其实就是一道题。。。
由于数据范围问题,我们只讲# P4597 序列 sequence 以及 # CF13C Sequence,# CF713C Sonya and Problem Wihtout a Legend
我们先考虑朴素的方法, 定义 \(f_{i,j}\) 为考虑 \(1 ~i\) 能满足是非降数列且最后一个数是 \(j\) 的最小花费。
则有 \(f_{i,j} = \min_{k \le j}\{f_{i-1,k} + |j - a_i|\}\) ,但是这是过不去的, ~但是# P2893 [USACO08FEB] Making the Grade G 是能过滴~。注意到只要设 \(g_i(j) = f_{i,j}\) 发现这 \(g_i\) 是满足 \(Slope Trick\) 的函数的条件!用因为 \(g_i\) 就是 \(g_{i - 1}\) 先取前缀再取 \(\min\) 得到的,显然用 \(Slope Trick\)呀!就做完了,按上面做就行了!
代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n;
int a[N];
priority_queue<int> heap;
int ans;
signed main()
{
cin >> n;
for(int i = 1 ; i <= n ; i ++ ) cin >> a[i];
for(int i = 1 ; i <= n ; i ++ )
{
if(heap.size() == 0)
{
heap.push(a[i]);
continue;
}
if(heap.top() > a[i])
{
ans += (heap.top() - a[i]);
heap.pop();
heap.push(a[i]);
}
heap.push(a[i]);
}
cout << ans;
return 0;
}
其实还有两道例题的,但是它是 # P3642 [APIO2016] 烟花表演 和# CF1787H Codeforces Scoreboard有点难慢慢啃 \(QwQ\).

浙公网安备 33010602011771号