斜率优化小总结
斜率优化dp 的小总结
首先下凸包/上凸包的求法可以使用单调队列/单调栈来实现。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5E5 + 5;
struct node {
int x, y;
} b[N];
int n, a[N], k, st[N], top;
double ans;
bool check(int i, int j, int k) {
return (b[j].y - b[i].y) * (b[k].x - b[j].x) > (b[k].y - b[j].y) * (b[j].x - b[i].x);
}
signed main() {
scanf("%lld%lld", &n, &k);
for (int i = 1, p = 0; i <= n; i++) {
scanf("%lld", a + i);
p += a[i];
b[i] = { i, p };
}
for (int i = k; i <= n; i++) {
while (top > 1 && check(st[top - 1], st[top], i - k)) top--;
st[++top] = i - k;
int l = 1, r = top;
while (l < r) {
int mid = l + r >> 1;
if (check(st[mid], i, st[mid + 1]))
l = mid + 1;
else
r = mid;
}
ans = max(ans, 1.0 * (b[i].y - b[st[l]].y) / (b[i].x - b[st[l]].x));
}
printf("%.2lf", ans);
return 0;
}
这个也非常的好求,那如何在dp
内优化呢?
这里引入一种 dp
方程是有一项是跟i
j
都有关的,例如 dpi=dpj+(ai−aj)2
这个就可以直接化成dpi=dpj+a2i+a2j−2∗ai∗aj
移项可得 dpj+a2j=dpi−a2i+2∗ai∗aj
令 y=dpi+a2i,x=ai,k=ai∗2
假设 k 不单调,使用单调栈加二分的算法。
如果单调,就可以直接单调队列。
而如果x 不单调,就可以直接用cdq分治
的方法来写。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1E5 + 5;
int n, h[N], w[N], p[N], dp[N], st[N];
struct node {
int x, y;
bool operator<(const node &a) const {
if (x != a.x)
return x < a.x;
return y < a.y;
}
} b[N], a[N];
bool check(int i, int j, int k) {
return (b[i].y - b[j].y) * (b[j].x - b[k].x) <= (b[j].y - b[k].y) * (b[i].x - b[j].x);
}
inline int calc(int i, int j) { return b[j].y + h[i] * h[i] + p[i - 1] - 2 * h[i] * b[j].x; }
void cdq(int l, int r) {
if (l == r)
return;
int mid = l + r >> 1;
cdq(l, mid);
for (int i = l; i <= mid; i++) b[i] = a[i];
sort(b + l, b + mid + 1);
int top = 0;
for (int i = l; i <= mid; i++) {
while (top > 1 && check(i, st[top], st[top - 1])) top--;
st[++top] = i;
}
for (int i = mid + 1; i <= r; i++) {
int L = 1, R = top;
while (L < R) {
int mid = L + R >> 1;
if (calc(i, st[mid]) > calc(i, st[mid + 1]))
L = mid + 1;
else
R = mid;
}
dp[i] = min(dp[i], calc(i, st[L]));
a[i] = { h[i], dp[i] + h[i] * h[i] - p[i] };
}
cdq(mid + 1, r);
}
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) scanf("%lld", h + i);
for (int i = 1; i <= n; i++) scanf("%lld", w + i), p[i] = p[i - 1] + w[i];
memset(dp, 0x3f, sizeof dp);
dp[1] = 0;
a[1] = { h[1], h[1] * h[1] - p[1] };
cdq(1, n);
cout << dp[n];
return 0;
}
Building Bridges cdq + 单调栈。时间复杂度当然是 (nlog2n)