P4655 [CEOI2017] Building Bridges 题解

明显是 dp。

首先列出 dp 式子,设 \(f_i\) 为跳到 \(i\) 时的最小代价。

那么:

\(f_i=f_j+(h_i-h_j)^2+(sum_{i-1}-sum_j)\)

\(f_i=f_j+h_i^2+h_j^2+2h_ih_j+sum_{i-1}-sum_j\)

\(f_j+h_j^2-sum_j=-2h_ih_j+f_i-h_i^2-sum_{i-1}\)

明显斜率式,考虑斜率优化,但 \(h_i,h_j\) 均无单调性。

那么就可以用斜率三件套了:李超线段树,平衡树,cdq 分治,这里选择 cdq 分治。

步骤如下:

1、对左边进行分治。

2、维护左边凸包。

3、对右边斜率排序。

4、对右边进行分治。

5、对整个区间的点按 \(x\) 排序,用来维护下次的凸包。

递归到边界时注意处理点的坐标

本题求最小值,所以维护下凸包,斜率从小到大排序,剩下的就没什么了。

由于懒,所以排序选择了 sort,比归并复杂度高,但问题不大。

时间复杂度:\(O(n\times\log_2^2(n))\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N=2e5+5;
int h[N],sum[N],n,s[N],f[N];
struct node
{
	int name,x,y;
}q[N];
int cmp(node fi,node se)
{
	if(fi.x==se.x)return fi.y>se.y;
	return fi.x<se.x;
}
int cmp2(node fi,node se)
{
	return h[fi.name]<h[se.name];
}
int cmp3(node fi,node se)
{
	return fi.name<se.name;
}
inline int clac(int x)
{
	return x*x;
}
double slope(int x,int y)
{
	if(q[x].x==q[y].x)return -1e18;
	return (q[x].y-q[y].y)*1.0/1.0/(q[x].x-q[y].x);
}
void cdq(int l,int r)
{
	if(l==r)
	{
		q[l].x=h[l];
		q[l].y=f[l]-sum[l]+clac(h[l]);
		return;
	}
	int mid=(l+r)>>1;
	cdq(l,mid);
	int lc=1,rc=0;
	for(int i=l;i<=mid;i++)
	{
		while(lc<rc&&slope(s[rc],s[rc-1])>=slope(i,s[rc]))rc--;
		s[++rc]=i;
	}
	sort(q+mid+1,q+r+1,cmp2);
	for(int i=mid+1;i<=r;i++)
	{
		while(lc<rc&&slope(s[lc],s[lc+1])<=2*h[q[i].name])lc++;
		f[q[i].name]=min(f[q[i].name],f[q[s[lc]].name]+sum[q[i].name-1]-sum[q[s[lc]].name]+clac(h[q[i].name]-h[q[s[lc]].name]));
	}
	sort(q+mid+1,q+r+1,cmp3);
	cdq(mid+1,r);
	sort(q+l,q+r+1,cmp);
}
signed main()
{
	memset(f,0x3f,sizeof(f));
	f[1]=0;
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&h[i]),q[i].name=i;
	for(int i=1;i<=n;i++)scanf("%lld",&sum[i]),sum[i]+=sum[i-1];
	cdq(1,n);
	printf("%lld",f[n]);
	return 0;
}
/*
10
1 2 3 4 5 4 3 2 1 0
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1
*/
posted @ 2023-02-24 13:44  Gmt丶Fu9ture  阅读(48)  评论(0)    收藏  举报