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
*/

浙公网安备 33010602011771号