前言
好久没做斜率优化的题了,找个水题来练练手顺便总结一下我对斜率优化的理解。
题目大意
现在有
N
个检查点和一个给定的序列
现在每个检查点必须要放塔或者木偶。问最小的花费。
N
<=
Ai
<=
106
解题思路
一看就知道是裸的斜率优化……
我们可先把序列翻转,设
Fi
表示做完前i个检查点,并且在第i个检查点放置一个塔的最小花费。转移式很简单
Fi=min(Fj+(i−j)∗(i−j−1)2)j∈[1,i)
。那么很明显要用斜率优化。
斜率优化-式子的化简
设
j>k
,那么如果
j
优于
Fj+(i−j)∗(i−j−1)2
<
Fk+(i−k)∗(i−k−1)2
2Fj+i2−2ij+j+j2
<
2Fk+i2−2ik+k+k2
我们设
Gj=2Fj+j+j2
,式子两边同时约掉
i2
得
Gj−2ij
<
Gk−2ik
Gj−Gk
<
i∗(2j−2k)
Gj−Gk2j−2k<i
(这显然就是斜率的式子)
斜率优化-单调性分析
然后就到了分析单调性的时候了,之前一直对怎么分析单调性一脸懵逼,所以现在仔细的写一写。
我们设
T(k,j)=Gj−Gk2j−2k
我们现在知道
Gj−Gk2j−2k<i
,我们观察在单调队列中连续的三项应该有什么关系,设这三项为
l,k,j(l<k<j)
,如果单调队列中的斜率是单调递减的,就是说
T(l,k)>T(k,j)
,那么如果
k
是最优解,那么
程序
// YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 1e6 + 5;
int N, A[MAXN], D[MAXN];
LL F[MAXN], G[MAXN];
double Slope(int x, int y) {
return 1.0 * (G[y] - G[x]) / (2.0 * (y - x));
}
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; i ++) scanf("%d", &A[i]);
for (int i = 1; i <= N / 2; i ++) swap(A[i], A[N - i + 1]);
int l = 1, r = 0;
LL Ans = 1ll << 60;
for (int i = 1; i <= N; i ++) {
while (l < r && Slope(D[l], D[l + 1]) < 1.0 * i) l ++;
int p = D[l];
F[i] = F[p] + 1ll * (i - p) * (i - p - 1) / 2 + A[i];
Ans = min(Ans, F[i] + 1ll * (N - i + 1) * (N - i) / 2);
G[i] = F[i] * 2 + i + 1ll * i * i;
while (l < r && Slope(D[r - 1], D[r]) > Slope(D[r], i)) r --;
D[++ r] = i;
}
printf("%lld", Ans);
}