【CDQ分治】【斜率优化】【DP】CEOI2017 Building_bridges

该博客介绍了CEOI2017 Building_bridges问题,内容涉及如何用动态规划(DP)结合斜率优化和CDQ分治策略解决柱子连接的最小代价问题。博主分析了当斜率(2×hi)不单调时,如何应用CDQ分治策略进行分治处理,并详细描述了算法步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出N个柱子,现在要顺次连接它们(从1连到N),每次连接的代价为高度之差的平方。

如果某些柱子不连,那么需要用Wi的代价销毁它。

求最小代价。

神奇的链接(一个叫陈万洲的抄袭狗的博客)
https://www.yunyii.cn/article/Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDU0MDY5L2FydGljbGUvZGV0YWlscy84MzM4MTk5Mg==


分析:

很显然的DP
很显然的斜率优化

推出来一坨东西后,发现需要满足的斜率 ( 2 × h i ) (2\times h_i) (2×hi)不是单调的。

所以。。。CDQ分治啊。。。

先按 h i h_i hi升序排列。

在处理 [ l , r ] [l,r] [l,r]区间时,先按照编号,分为较小的较大的两部分。(因为只有编号较小的会对编号较大的做出贡献)

然后先处理小的部分,把小的部分的DP值算出来。

然后再通过小的部分来更新大的部分的答案。

然后再处理右半部分。

当然,返回的时候不能忘了把顺序还原为按h值升序

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
struct node{
	ll x,y,id;	
}p[MAXN],tmp[MAXN];
int q[MAXN];
ll dp[MAXN],h[MAXN],w[MAXN],pre[MAXN];
int n;
bool sort_by_x(const node &a,const node &b){
	if(a.x!=b.x)
		return a.x<b.x;
	return a.id<b.id;
}
void CDQ(int l,int r){
//	PF("{%d %d}\n",l,r);
//	for(int i=1;i<=n;i++)
//		PF("[%lld %lld %lld %lld]\n",p[i].x,p[i].id,p[i].y,dp[p[i].id]);
//	PF("-------------------\n");
	if(l==r){
		p[l].y=dp[l]+h[l]*h[l]-pre[l];	
		return ;
	}
	int mid=(l+r)>>1;
	int xl=l,xr=mid+1;
	for(int i=l;i<=r;i++){
		if(p[i].id<=mid)
			tmp[xl++]=p[i];
		else
			tmp[xr++]=p[i];
	}
	for(int i=l;i<=r;i++)
		p[i]=tmp[i];
	CDQ(l,mid);
	int head=1,tail=0;
	for(int i=l;i<=mid;i++){
		while(head<tail&&1.0*(p[i].y-p[q[tail]].y)*(1.0*(p[q[tail]].x-p[q[tail-1]].x))<=1.0*(p[q[tail]].y-p[q[tail-1]].y)*(1.0*(p[i].x-p[q[tail]].x)))
			tail--;
		q[++tail]=i;
	}
	for(int id=mid+1;id<=r;id++){
		int i=p[id].id;
		while(head<tail&&1.0*(p[q[head+1]].y-p[q[head]].y)<=2.0*h[i]*(1.0*(p[q[head+1]].x-p[q[head]].x)))
			head++;
		int j=p[q[head]].id;
		dp[i]=min(dp[i],dp[j]+(h[i]-h[j])*(h[i]-h[j])+pre[i-1]-pre[j]);
	}
	CDQ(mid+1,r);
	xl=l,xr=mid+1;
	for(int i=l;i<=r;i++){
		if(xr>r||(xl<=mid&&p[xl].x<p[xr].x))
			tmp[i]=p[xl++];
		else
			tmp[i]=p[xr++];	
	}
	for(int i=l;i<=r;i++)
		p[i]=tmp[i];
}
int main(){
	memset(dp,0x3f,sizeof dp);
	SF("%d",&n);
	for(int i=1;i<=n;i++)
		SF("%lld",&h[i]);
	for(int i=1;i<=n;i++){
		SF("%lld",&w[i]);
		pre[i]=pre[i-1]+w[i];
	}
	for(int i=1;i<=n;i++){
		p[i].id=i;
		p[i].x=h[i];	
	}
	sort(p+1,p+1+n,sort_by_x);
	dp[1]=0;
	CDQ(1,n);
	PF("%lld",dp[n]);
} 
/*
6
3 8 7 1 6 6
0 -1 9 1 2 0
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值