P4655 [CEOI2017]Building Bridges 斜率优化

(4 mins to read)

有n根柱子,第i根柱子的高度为$h_i$​,被拆除的代价为$w_i$​,在j和i两根柱子间架桥的费用为$(h_i-h_j)^2 + \sum \limits_{k=j+1}^{i-1} w_k$​,问连接1和n的最小代价

做法

$dp_i = \min \limits_{j<i} \{dp_j + (h_i-h_j)^2 + s_{i-1} - s_j\}$ 此处s为w的前缀和显然可以化为斜率优化的形式每次询问$h_i$​处y的最小值,然后插入一条斜率为$-2h_i$​,截距为$h_i^2-s_i+dp_i$​的直线。由于这里的$h_i$​不具备单调性,所以不能用队列来维护。可以用平衡树、李超树或者cdq分治解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
int n, h[N];
ll w[N], dp[N];
bool Cmp;
struct Line
{
mutable ll k, b, p;
bool operator < (const Line& oth) const { return Cmp ? p < oth.p : k < oth.k; }
};
struct ConvecHull : multiset<Line>
{
static const ll inf = LLONG_MAX;
ll div(ll a, ll b)
{
if(!b) return a>0 ? inf : -inf;
return a / b - ((a ^ b) < 0 && a % b);
}
bool isect(iterator x, iterator y)
{
if (y == end()) { x->p = inf; return false; }
if (x->k == y->k) x->p = x->b > y->b ? inf : -inf;
else x->p = div((y->b - x->b), (x->k - y->k));
return x->p >= y->p;
}
void add(ll k, ll b)
{
auto z = insert({k, b, 0}), y = z++, x = y;
while (isect(y, z)) z = erase(z);
if (x != begin() && isect(--x, y)) isect(x, y = erase(y));
while ((y = x) != begin() && (--x)->p >= y->p) isect(x, erase(y));
}
ll qry(ll x)
{
Cmp = 1; auto l = *lower_bound({0, 0, x}); Cmp = 0;
return l.k * x + l.b;
}
}ch;
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", h+i);
for(int i=1; i<=n; i++) scanf("%lld", w+i), w[i] += w[i-1];
ch.add(2*h[1], -1ll*h[1]*h[1]+w[1]);
for(int i=2; i<=n; i++)
{
dp[i] = -ch.qry(h[i]) + w[i-1] + 1ll*h[i]*h[i];
ch.add(2*h[i], -1ll*h[i]*h[i]+w[i]-dp[i]);
}
printf("%lld\n", dp[n]);
return 0;
}