斜率优化
[HNOI 2008] 玩具装箱
给你一个转移方程:
其中
朴素方程是
考虑前缀和,设
转移复杂度是
我们把式子拆开,设
原式变成
把平方拆开
考虑一次函数
令
所以上式可以化成一次函数的形式
因为
我们发现当斜率为
再看为什么要维护凹壳,如图:
我们发现点
再推一个转移时用的式子:
我们称一个点更优当且仅当
对于
即有
至此,我们可以使用单调队列维护点集
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 500;
typedef long long ll;
ll c[N], pre[N], s[N], dp[N], L, Ll;
int qu[N];
int n, to = 1, tl = 1;
ll X(int i) {return s[i] + Ll;}
ll Y(int i) {return dp[i] + X(i) * X(i);}
ll KK(int i, int j) {return (Y(j) - Y(i)) / (X(j) - X(i));}
int main() {
scanf("%d %lld", &n, &L);
Ll = L + 1;
for(int i = 1;i <= n;i++) scanf("%lld", &c[i]);
for(int i = 1;i <= n;i++) pre[i] = pre[i - 1] + c[i];
for(int i = 1;i <= n;i++) s[i] = pre[i] + i;
for(int i = 1;i <= n;i++) {
while(to < tl && KK(qu[to], qu[to + 1]) < 2 * s[i]) to++;
dp[i] = dp[qu[to]] + (s[i] - X(qu[to])) * (s[i] - X(qu[to]));
while(to < tl && KK(qu[tl - 1], i) < KK(qu[tl - 1], qu[tl])) tl--;
qu[++tl] = i;
}
printf("%lld", dp[n]);
return 0;
}