P4655 [CEOI2017]Building Bridges

因为两桥不能相交,故易得(dp)方程:(dp[i]=min{dp[j]+sumw[i-1]-sumw[j]+(h[i]-h[j])^2})

看起来似乎可以斜率优化,可惜(h[i])不单调。

这里介绍一种很巧妙的做法:李超线段树。

整理方程:(dp[i]-h^2[i]-sumw[i-1]=(-2h[j])*h[i]+dp[j]-sumw[j]+h^2[j])

发现等式右侧的取值是斜率为(-2h[j]),截距为(dp[j]-sumw[j]+h^2[j])的直线。

相当于我们维护一个直线集,(dp[i])的最小取值即为(h[i])处的最小取值加(h^2[i]+sumw[i-1])

用李超线段树维护,时间复杂度(O(nlogn))

代码如下,仅供参考:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,h[maxn],sw[maxn],dp[maxn],lim,tr[maxn<<6];
struct node{int k,b;}p[maxn];
inline int calc(int id,int pos){
	if(id==0)return 1e16;
	return p[id].k*pos+p[id].b;
}
inline void update(int h,int l,int r,int x){
	if(!tr[h])return tr[h]=x,void();
	int mid=(l+r)>>1;
	int s=calc(tr[h],mid),t=calc(x,mid);
	if(l==r){
                if(s>t)tr[h]=x;
                return;
        }
	if(p[tr[h]].k<p[x].k){
		if(s<t)update(h<<1,l,mid,x);
		else update(h<<1|1,mid+1,r,tr[h]),tr[h]=x;
	}else{
		if(s<t)update(h<<1|1,mid+1,r,x);
		else update(h<<1,l,mid,tr[h]),tr[h]=x;
	}
}
inline int query(int h,int l,int r,int x){
	int mid=(l+r)>>1,val=calc(tr[h],x);
	if(l==r)return val;
	if(mid>=x)return min(val,query(h<<1,l,mid,x));
	else return min(val,query(h<<1|1,mid+1,r,x));
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		h[i]=read();
		lim=max(lim,h[i]);
	}
	for(int i=1;i<=n;i++)
		sw[i]=read()+sw[i-1];
	dp[1]=0;p[1]=(node){-2*h[1],dp[1]-sw[1]+h[1]*h[1]};
	update(1,0,lim,1);
	for(int i=2;i<=n;i++){
		dp[i]=query(1,0,lim,h[i])+sw[i-1]+h[i]*h[i];
		p[i]=(node){-2*h[i],dp[i]-sw[i]+h[i]*h[i]};
		update(1,0,lim,i);
	}
	printf("%lld
",dp[n]);
	return 0;
}

深深地感到自己的弱小。

原文地址:https://www.cnblogs.com/syzf2222/p/13880781.html