[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]);
}
posted @ 2021-11-05 13:28  leiyuanze  阅读(31)  评论(0编辑  收藏  举报