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\n",dp[n]);
	return 0;
}

深深地感到自己的弱小。

posted @ 2020-10-26 20:15  syzf2222  阅读(81)  评论(0编辑  收藏  举报