专栏文章

题解:P9691 [GDCPC 2023] Base Station Construction

P9691题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip8zg1x
此快照首次捕获于
2025/12/03 08:07
3 个月前
此快照最后确认于
2025/12/03 08:07
3 个月前
查看原文

单调队列优化 DP + 贪心

本题实际上就是给定若干条线段,要求在每个给出的线段中都至少要放一个点,求最后最小总代价是多少。
如果本题每个位置建立基站的权值都相同,则为经典贪心问题中的区间选点问题。
但本题每个位置建立基站的权值可以不同,于是不能直接按区间选点问题来处理,但是依然可以借助贪心思想。
考虑贪心:对于数轴中每个位置 ii,用 preipre_i 记录这个位置前面距离位置 ii 最近的上一个线段的左端点 jj,即使得 (j,i)(j,i) 这段范围内不存在完整的线段,这样 [j,i][j,i] 这段区间就是就是必须要放的点的区间了,并且在这个区间内放一个点就尽可能多地覆盖给定的线段。
prepre 数组的具体实现细节可见代码。
由于数轴中的位置值域不大,可以考虑 DP,并且由于存在多个上述的区间,需要考虑对于区间之间的转移,其实分析一下就会发现是一个单调队列优化 DP。
fif_i 表示数轴 ii 位置前面的必须放点的区间都已经放了且当前选择位置 ii 放点所需的最小总代价。
DP 转移显然:
fi=min{fj}+ai(preiji1)f_i = \min \{f_j\} + a_i \quad (pre_i \le j \le i-1)
最终答案即为 左端点最靠右的那条线段的左端点右端点最靠右的那条线段的右端点 中产生,因为在这个范围内选点一定对应整个要求的区间的点都选完了。
即:
ans=min{fi}(pren+1in)ans =\min \{ f_i \} \quad (pre_{n+1} \le i \le n)
时间复杂度 O(n)\mathcal O(n)
代码如下:
CPP
#define rep(x,y,z) for(int x=y;x<=z;x++)
typedef long long LL;
const int N=5e5+5;
int n,m;
int a[N];
int q[N];
LL f[N];
int pre[N];
void solve(){
	cin>>n;
	rep(i,1,n) cin>>a[i];
	rep(i,1,n+1) pre[i]=f[i]=0;
	cin>>m;
	while(m--){
		int l,r;
		cin>>l>>r;
		pre[r+1]=max(pre[r+1],l);
	}
	rep(i,1,n+1) pre[i]=max(pre[i],pre[i-1]);
	int hh=1,tt=0;
	rep(i,1,n){
		while(hh<=tt && f[q[tt]]>=f[i-1]) tt--;
		q[++tt]=i-1;
		while(q[hh]<pre[i]) hh++;
		f[i]=f[q[hh]]+a[i];
	}
	LL ans;
	memset(&ans,0x3f,sizeof ans);
	rep(i,pre[n+1],n) ans=min(ans,f[i]);
	cout<<ans;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...