( ext{Solution})
很容易想到 (dp)
[f_i = f_j + (h_i-h_j)^2 + sum_{k=i+1}^{j-1}w_k
]
令 (s_i = sum_{k=1}^i w_i)
则
[f_i = s_{i-1} + h_i^2 -2h_j imes h_i + f_j + h_j^2 - s_j
]
显然可以把决策 (j) 看成斜率为 (-2h_j) 截距为 (f_j + h_j^2 - s_j) 的直线
然后求 (x = h_i) 的最小值
李超线段树优化即可,(O(n log n))
当然这种 (dp) 看到平方项立刻就会想用斜率优化
维护下凸包,发现需要用平衡树维护,或者用 (CDQ) 分治处理
这题就成了眼切的题了
李超线段树做法
( ext{Code})
#include <cstdio>
#include <iostream>
#define ls (p << 1)
#define rs (ls | 1)
#define re register
using namespace std;
typedef long long LL;
const int N = 1000010;
int n, len, h[N], w[N], fl[N << 2]; LL f[N], s[N];
struct line{LL k , b;}seg[N << 2];
inline double Isc(LL k1, LL b1, LL k2, LL b2){return 1.0 * (b2 - b1) / (k1 - k2);}
void update(int p, int l, int r, LL k, LL b)
{
if (!fl[p]){seg[p] = line{k, b}, fl[p] = 1; return;}
LL f1 = seg[p].k * l + seg[p].b, f2 = seg[p].k * r + seg[p].b, f3 = k * l + b , f4 = k * r + b;
if (f3 >= f1 && f4 >= f2) return;
else if (f3 <= f1 && f4 <= f2) return void(seg[p] = line{k, b});
int mid = (l + r) >> 1; double len = Isc(k, b, seg[p].k, seg[p].b);
if (f3 <= f1)
{
if (len <= mid) update(ls, l, mid, k, b);
else update(rs, mid + 1, r, seg[p].k, seg[p].b), seg[p] = line{k, b};
}
else{
if (len > mid) update(rs, mid + 1, r, k, b);
else update(ls, l, mid, seg[p].k, seg[p].b), seg[p] = line{k, b};
}
}
LL query(int p, int l, int r, int x)
{
LL ans = seg[p].k * x + seg[p].b;
if (l == r) return ans;
int mid = (l + r) >> 1;
if (x <= mid) ans = min(ans , query(ls, l, mid, x));
else ans = min(ans , query(rs, mid + 1, r, x));
return ans;
}
int main()
{
scanf("%d", &n);
for(re int i = 1; i <= n; i++) scanf("%d", &h[i]), len = max(h[i], len);
for(re int i = 1; i <= n; i++) scanf("%d", &w[i]), s[i] = s[i - 1] + w[i];
for(re int i = 1; i <= 4000001; i++) seg[i].b = 1e18 , seg[i].k = 0;
update(1, 0, len, -2LL * h[1], 1LL * h[1] * h[1] - s[1]);
for(re int i = 2; i <= n; i++)
{
f[i] = s[i - 1] + 1LL * h[i] * h[i] + query(1, 0, len, h[i]);
if (i ^ n) update(1, 0, len, -2LL * h[i], f[i] + 1LL * h[i] * h[i] - s[i]);
}
printf("%lld
", f[n]);
}