洛谷 P4655 [CEOI2017]Building Bridges

有 \(n\) 根柱子依次排列,每根柱子都有一个高度。第 \(i\) 根柱子的高度为 \(h_i\)。

现在想要建造若干座桥,如果一座桥架在第 \(i\) 根柱子和第 \(j\) 根柱子之间,那么需要 \((h_i-h_j)^2\)​​ 的代价。

在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第 \(i\) 根柱子被拆除的代价为 \(w_i\),注意 \(w_i\) 不一定非负,因为可能*希望拆除某些柱子。

现在*想要知道,通过桥梁把第 \(1\) 根柱子和第 \(n\) 根柱子连接的最小代价。注意桥梁不能在端点以外的任何地方相交。

首先考虑dp,设\(f_i\)表示第\(i\)个柱子和第\(1\)个柱子连接所需要的最小代价,那么可以写出状态转移方程:

\[f_i=Min_{j=1}^{i-1}f_j+sw_{i-1}-sw_j+(h_i-h_j)^2 \]

其中\(sw_i=\sum_{j=1}^iw_j\),然后对式子稍微变一下形:

\[f_i=sw_{i-1}+h_i^2+Min_{j=1}^{i-1}-2h_jh_i+f_j-sw_j+h_j \]

后面的部分可以看成一条关于\(h_i\)的线段,于是我们可以直接用李超树维护。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e6 + 10;
const long long inf = 1e16;
using namespace std;
int n,h[N + 5],w[N + 5],cc;
long long sw[N + 5],f[N + 5];
double b[N + 5],k[N + 5];
double val(int id,int x)
{
	if (!id)
		return inf;
	return k[id] * x + b[id];
}
bool cmp(int a,int b,int x)
{
	double fa = val(a,x),fb = val(b,x);
	return fa < fb;
}
struct Seg
{
	#define zrt k << 1
	#define yrt k << 1 | 1
	int mx[N * 5 + 5];
	void update(int k,int l,int r,int x,int y,int nw)
	{
		if (r < x || l > y)
			return;
		if (l >= x && r <= y)
		{
			if (cmp(mx[k],nw,l) && cmp(mx[k],nw,r))
				return;
			if (cmp(nw,mx[k],l) && cmp(nw,mx[k],r))
			{
				mx[k] = nw;
				return;
			}
			int mid = l + r >> 1;
			if (cmp(nw,mx[k],mid))
				swap(nw,mx[k]);
			if (cmp(nw,mx[k],l))
				update(zrt,l,mid,x,y,nw);
			else
				update(yrt,mid + 1,r,x,y,nw);
			return;
		}
		int mid = l + r >> 1;
		update(zrt,l,mid,x,y,nw);
		update(yrt,mid + 1,r,x,y,nw);
	}
	int query(int k,int l,int r,int x)
	{
		if (x < l || x > r)
			return 0;
		if (l == r && l == x)
			return mx[k];
		int mid = l + r >> 1,res;
		if (x <= mid)
			res = query(zrt,l,mid,x);
		else
			res = query(yrt,mid + 1,r,x);
		if (cmp(mx[k],res,x))
			res = mx[k];
		return res;
	}
}tree;
int main()
{
	scanf("%d",&n);
	for (int i = 1;i <= n;i++)
		scanf("%d",&h[i]);
	for (int i = 1;i <= n;i++)
	{
		scanf("%d",&w[i]);
		sw[i] = sw[i - 1] + w[i];
	}
	cc++;
	k[cc] = -2 * h[1];
	b[cc] = -sw[1] + 1.0 * h[1] * h[1];
	tree.update(1,0,N,0,N,cc);
	for (int i = 2;i <= n;i++)
	{
		int num = tree.query(1,0,N,h[i]);
		f[i] = sw[i - 1] + 1ll * h[i] * h[i] + val(num,h[i]);
		cc++;
		k[cc] = -2 * h[i];
		b[cc] = f[i] - sw[i] + 1.0 * h[i] * h[i];
		tree.update(1,0,N,0,N,cc);
	}
	cout<<f[n]<<endl;
	return 0;
}
上一篇:华为交换机配置管理VLAN及管理IP


下一篇:二分法查表