2023-2024 ICPC East North America Regional Contest (ECNA 2023) B. B Road Band(线性dp 决策单调性分治优化)

题目

给定两条平行轴上的点,两条轴的距离是s(s<=50)

第一条轴上给定m(m<=1000)个点,第二条轴上给定n(n<=1000)个点,点坐标x(0<=x<=1000)

在距两条轴距离相等且平行的线上,标记k(k<=100)个位置点,

使得n+m个点,点i对k个位置中距离最近的点算距离,记为xi

要求\sum_{i=1}^{n+m}x_{i}^2最小,并输出这个值,误差不超过1e-5

思路来源

gym AC代码

【学习笔记】各类基于决策单调性的dp优化_决策单调性优化dp_sophilex的博客-CSDN博客

题解

首先,由于轴两侧是等距的,可以把轴上的点合到其中一侧并排序,

d即线上修建k个点,轴上n+m个点,到线的距离为s/2,每个点到最近点的距离平方的和最小

那么每个修建的点对应的肯定是一段连续的区间,枚举最后一个修建点对应了哪一段朴素dp即可

为了确定修建点的位置,

考虑(a_{i},\frac{s}{2})(x,0)的距离的平方,为(a_{i}-x)^2+(\frac{s}{2})^2

(\frac{s}{2})^2是固定的,可以最后加上,考虑怎么求前一项,

y=(a_{1}-x)^2+(a_{2}-x)^2+...+(a_{i}-x)^2的最小值,

三分是理论可行的,但是写了一发交上去t了,原因在double还是比较耗时的

所以考虑对y求导,2(a_{1}-x)+2(a_{2}-x)+...+2(a_{i}-x)=0

x=\frac{\sum_{j=1}^{i}a_{j}}{i},将所求式展开,分别为\sum a_{i}^2i*x^2-2*x \sum a_{i}

分别维护前缀和、前缀平方和,即可O(1)求得最小值

然后朴素dp即可,枚举最后一段,复杂度O((n+m)*(n+m)*k)

优化

主要是学习一下分治优化dp,感觉本题就是一个决策单调性的典题

可将mid所在决策选点时,与其共处一个区间的最左左端点,看成是决策点

则决策点只有[1,n]这些种,套用分治优化决策单调性即可

复杂度O((n+m)*log(n+m)*k)

代码1(朴素dp)

// Problem: B. B Road Band
// Contest: Codeforces - 2023-2024 ICPC East North America Regional Contest (ECNA 2023)
// URL: https://codeforces.com/gym/104757/problem/B
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int K=105,N=2e3+10;
const db eps=1e-3;
int m,n,k,s;
db a[N],dis[N][N],pos[N][N],dp[K][N],sum[N],sum2[N];
db cal(int l,int r){
	db mid=(sum[r]-sum[l-1])/(r-l+1);
    return (sum2[r]-sum2[l-1])-2*mid*(sum[r]-sum[l-1])+(r-l+1)*mid*mid;
}
void sol(){
	sci(m);sci(n),sci(k),sci(s);
	rep(i,1,m)scanf("%lf",&a[i]);
	rep(i,m+1,m+n)scanf("%lf",&a[i]);
	m+=n;
	sort(a+1,a+m+1);
	for(int i=1;i<=m;i++){
        sum[i]=sum[i-1]+a[i];
        sum2[i]=sum2[i-1]+a[i]*a[i];
    }
	rep(i,1,m){
		rep(j,i,m){
			dis[i][j]=cal(i,j);
			//printf("i:%d j:%d dis:%lf\n",i,j,dis[i][j]);
		}
	}
	rep(i,1,m)dp[0][i]=8e18;
	rep(i,1,k){
		rep(j,1,m){
			dp[i][j]=8e18;
			rep(x,1,j){
				dp[i][j]=min(dp[i][j],dp[i-1][j-x]+dis[j-x+1][j]);
			}
			//printf("i:%d j:%d dp:%lf\n",i,j,dp[i][j]);
		}
	}
	db ans=dp[k][m]+m*(s/2.)*(s/2.);
	printf("%.10lf\n",ans);
}
int main(){
	sol();
	return 0;
}

代码2(决策单调性 分治优化)

// Problem: B. B Road Band
// Contest: Codeforces - 2023-2024 ICPC East North America Regional Contest (ECNA 2023)
// URL: https://codeforces.com/gym/104757/problem/B
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int K=105,N=2e3+10;
const db eps=1e-3;
int m,n,k,s;
db a[N],pos[N][N],dp[N],sum[N],sum2[N],dp2[N];
db cal(int l,int r){
	db mid=(sum[r]-sum[l-1])/(r-l+1);
    return (sum2[r]-sum2[l-1])-2*mid*(sum[r]-sum[l-1])+(r-l+1)*mid*mid;
}
void dfs(int l,int r,int pl,int pr){
  	int mid=(l+r)/2,p=pl;
    db res=dp[pl]+cal(pl+1,mid);
    for(int j=pl+1;j<=min(mid-1,pr);j++){
        double tmp=dp[j]+cal(j+1,mid);
        if(tmp<res){
            res=tmp;
            p=j;
        }
    }
    dp2[mid]=res;
    if(l<mid)dfs(l,mid-1,pl,p);
    if(mid<r)dfs(mid+1,r,p,pr);
}
void sol(){
	sci(m);sci(n),sci(k),sci(s);
	rep(i,1,m)scanf("%lf",&a[i]);
	rep(i,m+1,m+n)scanf("%lf",&a[i]);
	m+=n;
	sort(a+1,a+m+1);
	for(int i=1;i<=m;i++){
        sum[i]=sum[i-1]+a[i];
        sum2[i]=sum2[i-1]+a[i]*a[i];
    }
	rep(i,0,m)dp[i]=dp2[i]=8e18;
	dp[0]=0;
    rep(i,1,k){
        dfs(1,m,0,m);
    	swap(dp,dp2);
    }
	db ans=dp[m]+m*(s/2.)*(s/2.);
	printf("%.10lf\n",ans);
}
int main(){
	sol();
	return 0;
}

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值