Link
P4655 [CEOI2017]Building Bridges
Solve
我们很容易能退出转移方程
[dp[i]=min(dp[i],dp[j]+(H[i]-H[j])ast (H[i]-H[j])+S[i-1]-S[j])
]
[dp[i]=dp[j]-2ast H[i]ast H[j]+H[j]ast H[j]+H[i]ast [i]+S[i-1]-S[j]
]
[(2ast H[i]) * H[j] + (dp[i]-S[i-1]-H[i]ast H[i]) = (dp[j]+H[j]ast H[j]-S[j])
]
此时就能发现是斜率优化了。
但是这里的的k[i]和x都不递增,所以用(CDQ)或者线段树来维护,我用的是(CDQ)来维护
Code
#include<algorithm>
#include<cstring>
#include<cstdio>
#define LD long double
#define LL long long
#define Re register int
#define S2(a) (1ll*(a)*(a))
using namespace std;
const LL N=1e5+3,inf=1e18;
int n,H[N],W[N],Q[N];LL S[N],dp[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
//dp[i]=min(dp[i],dp[j]+(H[i]-H[j])*(H[i]-H[j])+S[i-1]-S[j]);
//dp[i]=dp[j]-2*H[i]*H[j]+H[j]*H[j]+H[i]*H[i]+S[i-1]-S[j]
//(2*H[i]) * H[j] + (dp[i]-S[i-1]-H[i]*H[i]) = (dp[j]+H[j]*H[j]-S[j])
// k * x + b = y
#define X(j) (a[j].x)
#define Y(j) (a[j].y)
struct QAQ{
int k,x,id;LL y;
inline bool operator<(const QAQ &O)const{return x!=O.x?x<O.x:y<O.y;}
}a[N],b[N];
inline bool cmp(QAQ A,QAQ B){return A.k<B.k;}
inline LD slope(Re i,Re j){return X(i)==X(j)?(Y(j)>Y(i)?inf:-inf):(LD)(Y(j)-Y(i))/(X(j)-X(i));}
inline void CDQ(Re L,Re R){
if(L==R){Re j=a[L].id;a[L].y=dp[j]+(LL)H[j]*H[j]-S[j];return;}//此时dp[j]必定已经求出来了,直接算计Y(j)即可
Re mid=L+R>>1,p1=L,p2=mid+1,h=1,t=0;
for(Re i=L;i<=R;++i)a[i].id<=mid?b[p1++]=a[i]:b[p2++]=a[i];//按照i的大小分到左右两边(两边的k0[i]分别递增)
for(Re i=L;i<=R;++i)a[i]=b[i];
CDQ(L,mid);//处理完左边后,左边的X(j)是递增的,此时右边还没处理,所以右边k0[i]是递增的
for(Re i=L;i<=mid;++i){//把左边的点拿出来维护凸包(使用单调队列)
while(h<t&&slope(Q[t-1],Q[t])>=slope(Q[t-1],i))--t;
Q[++t]=i;
}
for(Re i=mid+1,j,id;i<=R;++i){//把右边的点拿来决策(依旧是单调队列)
while(h<t&&slope(Q[h],Q[h+1])<=a[i].k)++h;
if(h<=t)id=a[i].id,j=Q[h],dp[id]=min(dp[id],a[j].y-(LL)a[i].k*a[j].x+S[id-1]+(LL)H[id]*H[id]);
}
CDQ(mid+1,R);//处理完右边后,两边都按照X(j)排好了序
Re w=L-1;p1=L,p2=mid+1;//把两边按照X(j)从小到大归并起来
while(p1<=mid&&p2<=R)b[++w]=a[p1]<a[p2]?a[p1++]:a[p2++];
while(p1<=mid)b[++w]=a[p1++];while(p2<=R)b[++w]=a[p2++];
for(Re i=L;i<=R;++i)a[i]=b[i];
}
int main(){
freopen("P4655.in","r",stdin);
freopen("P4655.out","w",stdout);
in(n);
for(Re i=1;i<=n;++i)in(H[i]);
for(Re i=1;i<=n;++i)in(W[i]);
for(Re i=1;i<=n;++i)S[i]=S[i-1]+W[i],dp[i]=inf;
for(Re i=1;i<=n;++i)a[i].k=(H[i]<<1),a[i].x=H[i],a[i].id=i;
sort(a+1,a+n+1,cmp);//先按k0[i]排序
dp[1]=0,CDQ(1,n);//注意左边界上的点要单独求
printf("%lld
",dp[n]);
}