[CEOI2017] Building Bridges
传送门
Description
有 \(n\)根柱子依次排列,每根柱子都有一个高度。第 \(i\) 根柱子的高度为 \(h_i\)。
现在想要建造若干座桥,如果一座桥架在第 ii 根柱子和第 jj 根柱子之间,那么需要 \((h_i-h_j)^2\) 的代价。
在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第 \(i\) 根柱子被拆除的代价为 \(w_i\),注意 \(w_i\)不一定非负,因为可能政府希望拆除某些柱子。
现在政府想要知道,通过桥梁把第 \(1\) 根柱子和第 \(n\) 根柱子连接的最小代价。注意桥梁不能在端点以外的任何地方相交。
Solution
一道水题写了好久,小难受。。。
发现那个\(w_i\)好烦,干脆最后全部加上个\(\sum _i\),在成为桥柱的地方减掉相应的\(w_i\)
此题显然是需要\(cdq\)分治的,或者动态凸包
按照惯例,\(dp\)题目是不会展现转移式子的,毕竟
都很好写注意就是求斜率的时候一定要考虑斜率不存在的情况!!!
Code
#include<bits/stdc++.h>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define reg register
#define db long double
#define ll long long
#define ri reg int i
#define int ll
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=1e5+5;
int n,w[MN],X[MN],Y[MN],f[MN],sum;
bool cmpl(int i,int j){return X[i]<X[j];}
bool cmpr(int i,int j){return X[i]<X[j];}
db calc(int i,int j){if(X[i]==X[j])return Y[i]<Y[j]?-1e18:1e18;return (db)(Y[i]-Y[j])/(X[i]-X[j]);}
int sqr(int x){return x*x;}
int cal(int i,int j){return f[j]+sqr(X[j]-X[i])-w[i];}
int lrk[MN],rrk[MN];
int q[MN],tl,hd;
void cdq(int l,int r)
{
if(l==r){Y[l]=f[l]+sqr(X[l]);return;}
reg int mid=(l+r)>>1,i,j;
cdq(l,mid);
for(i=l;i<=mid;++i) lrk[i]=i;
std::sort(lrk+l,lrk+mid+1,cmpl);
for(i=mid+1;i<=r;++i) rrk[i]=i;
std::sort(rrk+mid+1,rrk+r+1,cmpr);
for(tl=0,i=l;i<=mid;++i)
{
while(tl>1&&calc(q[tl],q[tl-1])>=calc(lrk[i],q[tl])) --tl;
q[++tl]=lrk[i];
}
for(hd=1,i=mid+1;i<=r;++i)
{
while(hd<tl&&calc(q[hd+1],q[hd])<=(db)2.*X[rrk[i]]) ++hd;
f[rrk[i]]=min(f[rrk[i]],cal(rrk[i],q[hd]));
}
cdq(mid+1,r);
}
signed main()
{
n=read();reg int i;
for(i=1;i<=n;++i) X[i]=read(),f[i]=1ll<<60;
for(i=1;i<=n;++i) w[i]=read(),sum+=w[i];
f[1]=-w[1];
cdq(1,n);
printf("%lld\n",sum+f[n]);
return 0;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!
致虚极,守静笃,万物并作,吾以观其复