[CEOI2017] Building Bridges
阿新 • • 發佈:2021-11-06
\(\text{Solution}\)
很容易想到 \(dp\)
令 \(s_i = \sum_{k=1}^i w_i\)
則
顯然可以把決策 \(j\) 看成斜率為 \(-2h_j\) 截距為 \(f_j + h_j^2 - s_j\) 的直線
然後求 \(x = h_i\) 的最小值
李超線段樹優化即可,\(O(n \log n)\)
當然這種 \(dp\) 看到平方項立刻就會想用斜率優化
維護下凸包,發現需要用平衡樹維護,或者用 \(CDQ\)
這題就成了眼切的題了
李超線段樹做法
\(\text{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\n", f[n]); }