#李超线段树 or 斜率优化+CDQ分治#洛谷 4655 [CEOI2017]Building Bridges 分析 代码(李超线段树) 代码(斜率优化)

题目


列出方程即为(dp[i]=min{dp[j]+(h[i]-h[j])^2+s[i-1]-s[j]})

(dp[j]+h[j]^2-s[j]=2*h[i]*h[j]+dp[i]-s[i-1]-h[i]^2)

那这就是一个斜率为(2*h[i]),截距为(dp[i]-s[i-1]-h[i]^2)的直线

那要使截距尽量小,考虑用李超线段树解决

否则由于(h[i])不具有单调性,考虑CDQ分治,第一维是时间,第二维是(h),斜率优化做


代码(李超线段树)

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
typedef long long lll;
const int N=100011,M=1000011;
struct rec{lll a,b;}line[N];
lll h[N],s[N],dp[N]; int p[N*40],n;
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void print(lll ans){
	if (ans<0) putchar('-'),ans=-ans;
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline lll calc(int t,int x){return line[t].a*x+line[t].b;}
inline signed either(int t1,int t2,int x){return calc(t1,x)<calc(t2,x)?t1:t2;}
inline void update(int k,int l,int r,int x,int y,int z){
	rr int mid=(l+r)>>1;
	if (x<=l&&r<=y){
		if (!p[k]) {p[k]=z; return;}
		rr lll la=calc(p[k],l),lb=calc(z,l);
		rr lll ra=calc(p[k],r),rb=calc(z,r);
		if (la<=lb&&ra<=rb) return;
		if (la>=lb&&ra>=rb){p[k]=z; return;}
		rr double pos=(line[p[k]].b-line[z].b)*1.0/(line[z].a-line[p[k]].a);
		if (la>=lb){
			if (pos<=mid) update(k<<1,l,mid,x,y,z);
			    else update(k<<1|1,mid+1,r,x,y,p[k]),p[k]=z;
		}else{
			if (pos>mid) update(k<<1|1,mid+1,r,x,y,z);
			    else update(k<<1,l,mid,x,y,p[k]),p[k]=z;
		}
		return;
	}
	if (x<=mid) update(k<<1,l,mid,x,y,z);
	if (mid<y) update(k<<1|1,mid+1,r,x,y,z);
}
inline signed query(int k,int l,int r,int x){
	if (l==r) return p[k];
	rr int mid=(l+r)>>1;
	if (x<=mid) return either(p[k],query(k<<1,l,mid,x),x);
	    else return either(p[k],query(k<<1|1,mid+1,r,x),x);
}
signed main(){
	n=iut(),line[0]=(rec){0,1000000000000000000ll};
	for (rr int i=1;i<=n;++i) h[i]=iut();
	for (rr int i=1;i<=n;++i) s[i]=s[i-1]+iut();
	dp[1]=0,line[1]=(rec){-h[1]<<1,h[1]*h[1]-s[1]},update(1,0,M,0,M,1);
	for (rr int i=2;i<=n;++i){
		dp[i]=h[i]*h[i]+s[i-1]+calc(query(1,0,M,h[i]),h[i]);
		line[i]=(rec){-h[i]<<1,dp[i]+h[i]*h[i]-s[i]},update(1,0,M,0,M,i);
	}
	return !printf("%lld",dp[n]);
} 

代码(斜率优化)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm> 
#define rr register
using namespace std;
typedef long long lll;
const int N=100011; const lll inf=1ll<<60;
int X[N],h[N],a[N],n,b[N],q[N]; lll s[N],dp[N],Y[N];
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline lll O(int x){return 1ll*x*x;}
inline lll min(lll a,lll b){return a<b?a:b;}
bool cmp(int x,int y){return h[x]<h[y];}
inline double slope(int i,int j){return (X[i]==X[j])?(Y[j]>=Y[i]?inf:-inf):((double)(Y[j]-Y[i])/(X[j]-X[i]));}
inline void cdq(int l,int r){
	if (l==r){
		Y[a[l]]=dp[a[l]]+O(h[a[l]])-s[a[l]];
		return;
	}
	rr int mid=(l+r)>>1,i1=l,j1=mid+1;
	for (rr int i=l;i<=r;++i)
	if (a[i]<=mid) b[i1++]=a[i];
	    else b[j1++]=a[i];
	for (rr int i=l;i<=r;++i) a[i]=b[i];
	cdq(l,mid);
	rr int head=1,tail=0,t=l-1;
	for (rr int i=l;i<=mid;++i){
		while (head<tail&&slope(q[tail-1],q[tail])>=slope(q[tail],a[i])) --tail;
		q[++tail]=a[i];
	}
	for (rr int i=mid+1,I=a[i];i<=r;I=a[++i]){
		while (head<tail&&slope(q[head],q[head+1])<=2*h[I]) ++head;
		if (head<=tail) dp[I]=min(dp[I],dp[q[head]]+O(h[I]-h[q[head]])+s[I-1]-s[q[head]]);
	}
	cdq(mid+1,r);
	for (i1=l,j1=mid+1;i1<=mid&&j1<=r;)
	if (X[a[i1]]<X[a[j1]]) b[++t]=a[i1++];
	    else b[++t]=a[j1++];
	while (i1<=mid) b[++t]=a[i1++];
	while (j1<=r) b[++t]=a[j1++];
	for (rr int i=l;i<=r;++i) a[i]=b[i];
}
signed main(){
	n=iut(),memset(dp,0x3f,sizeof(dp));
	for (rr int i=1;i<=n;++i) h[i]=iut();
	for (rr int i=1;i<=n;++i) s[i]=s[i-1]+iut();
	for (rr int i=1;i<=n;++i) X[i]=h[i],a[i]=i;
	sort(a+1,a+1+n,cmp),dp[1]=0,cdq(1,n);
	return !printf("%lld",dp[n]);
}