1. 程式人生 > 其它 >[CEOI2017] Building Bridges

[CEOI2017] Building Bridges

\(\text{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 \times 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\)

分治處理
這題就成了眼切的題了

李超線段樹做法

\(\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]);
}