状态定义很简单,
然后经过一堆花里胡哨的运算,就可以算出方程
f[j]-sum[j]+a[j+1]*j = a[j+1]*i-sum[i]+f[i]
设 y(j) = f[j]-sum[j]+a[j+1]*j
然后经过一堆花里胡哨的证明就可以发现y(j), i, sum[i]都是单增的,然后就可以找出斜率优化的两个条件之一,
更优解比较
(y(q[l + 1]) - y(q[l])) <=i * (a[q[l + 1] + 1] - a[q[l] + 1])
(通过解方程:f[i] (从q[l+1]转移) <= f[i] (从q[l]转移)的方程得出)
斜率单调的比较
(a[now + 1] - a[q[r] + 1])(y(q[r]) - y(q[r - 1])) >=(a[q[r] + 1] - a[q[r - 1] + 1])(y(now) - y(q[r]))
最后是代码
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#include <stack>
#define INF 0x3f3f3f3f
#define IMAX 2147483646
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int
using namespace::std;
const int maxn = 211111;
ll a[511111], sum[511111], n, k, t;
ll f[511111], now, q[511111], l, r;
#define y(j) (f[j] + a[j + 1] * j - sum[j])
int main() {
scanf("%lld", &t);
while (t--) {
scanf("%lld%lld", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%lld", a + i), sum[i] = sum[i - 1] + a[i];
l = r = 1;
q[1] = k;
for (int i = k; i <= n; i++) {
if (i < k * 2) { f[i] = sum[i] - i * a[1]; continue; }
while (l < r && (y(q[l + 1]) - y(q[l])) <=
i * (a[q[l + 1] + 1] - a[q[l] + 1]))l++;
f[i] = f[q[l]] + sum[i] - sum[q[l]] - a[q[l] + 1] * (i - q[l]);
now = i - k + 1;
while (l < r && (a[now + 1] - a[q[r] + 1])*(y(q[r]) - y(q[r - 1])) >=
(a[q[r] + 1] - a[q[r - 1] + 1])*(y(now) - y(q[r])))r--;
q[++r] = now;
}
printf("%lld\n", f[n]);
}
}