10086

my first title

my first paragraph

[ZJOI2010] 基站选址

题目

题目最刁钻也是最核心的条件 : “如果在距离第 i 个村庄不超过 si 的范围内建立了一个通讯基站,那么就成它被覆盖了”

首先暴力:
  • 状态 fi,j 表示在 j 建了第 i 个基站,目前的花费是多少
  • fi,jfi1,k+cost(j,k)+cj
  • 其中 cost(j,k) 可以 O(n3) 预处理
分析

时间复杂度瓶颈在于 cost 的处理。

我们以上转移的过程花费统计是从给定端点出发,而考虑到条件里的覆盖与否只与 i 本身有关,我们得到 j<Li,Ri<k 时会统计入 wi 的贡献。

我们化繁为简,这就变为了一个类似二维数点的东西。

重新回顾 dp 的过程,令当前阶段为 i:

  1. 当我们扫到 j 这一处基站时,应当从 [i1,j1] 这个区间的最优点转移过来,故数据结构上的每个位置 s 维护的是:上层 dp 值加上 cost(s,j),即暴力中的 fi1,k+cost(j,k)
  2. 接着,对于先前 Rk=j 的一系列点 k,在此之后的转移中:
    • 满足了 Rk<j,那么对于小于 Lk 的位置,就要加入 wk 的花费了。
具体实现:
  1. 处理出每个点的 L,R,并跟据 R 排序,本人用 vector 实现,邻接表亦可;

  2. dp 部分

    • 将上一阶段的结果放入数据结构中
    • 从左到右枚举设立基站的位置,先询问 [i1,j1] 的最优结果。遍历 Rk=j 的点,在 [1,Lk1] 加上 wk 的价值
  3. 细节处理,由于要保证后面未统计部分记入答案,不妨 ++n,++k 最后直接统计 f_n 的结果

数据结构区间加,区间查询,继承结果,故使用线段树。时间复杂度 O(nklog2n)

反思:
  1. 数据结构优化 dp,我们可以将上一阶段 dp 放入数据结构中,进行转移。
  2. 对于式子,我们应当化繁为简,找出如二维偏序等重要特征

贴上代码:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 2e4 + 10,inf = 0x3f3f3f3f;

inline int read(){
	register int x = 0,f = 1;
	register char c = getchar();
	while(c<'0'||c>'9'){ if(c == '-')f = -f; c = getchar(); }
	while(c>='0'&&c<='9')x = (x<<3) + (x<<1) + (c^48),c = getchar();
	return x * f;
}

int d[N],c[N],s[N],w[N];
int l[N],r[N];
int n,k,ans;
int f[N];
vector<int> e[N];

struct Segment{
	int val[N<<2],tag[N<<2];
	#define ls (p<<1)
	#define rs (p<<1|1)
	void pushup(int p){ val[p] = min(val[ls],val[rs]); }
	void pushdown(int p){
		if(!tag[p])return ;
		val[ls] += tag[p], val[rs] += tag[p];
		tag[ls] += tag[p], tag[rs] += tag[p];
		tag[p] = 0;
	}
	void build(int p,int l,int r){
		tag[p] = 0,val[p] = 0;
		if(l == r)return val[p] = f[l],void();
		int mid = l + r >> 1;
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(p);
	}
	void modify(int p,int l,int r,int L,int R,int v){
		if(L <= l && r <= R)return val[p] += v, tag[p] += v, void();
		pushdown(p);
		int mid = l + r >> 1;
		if(L <= mid)modify(ls,l,mid,L,R,v);
		if(mid < R)modify(rs,mid+1,r,L,R,v);
		pushup(p);
	}
	int query(int p,int l,int r,int L,int R){
		if(L <= l && r <= R)return val[p];
		pushdown(p);
		int mid = l + r >> 1, res = inf;
		if(L <= mid)res = query(ls,l,mid,L,R);
		if(mid < R)res = min(res,query(rs,mid+1,r,L,R));
		return res;
	}
}seg;



int main(){
	n = read(),k = read()+1;
	for(int i = 2;i<=n;++i)d[i] = read();
	for(int i = 1;i<=n;++i)c[i] = read();
	for(int i = 1;i<=n;++i)s[i] = read();
	for(int i = 1;i<=n;++i)w[i] = read(),ans += w[i];



	++n; d[n] = w[n] = inf;
	
	
	for(int i = 1;i<=n;++i)
		l[i] = lower_bound(d+1,d+n+1,d[i]-s[i])-d,
		r[i] = upper_bound(d+1,d+n+1,d[i]+s[i])-d-1,
		e[r[i]].push_back(i);
	
	int res = 0;
	for(int j = 1;j<=n;++j){
		f[j] = res + c[j];
		for(int x : e[j])res += w[x];
	}
	ans = f[n];
	for(int i = 2;i<=k;++i){
		seg.build(1,1,n);
		for(int j = 1;j<=n;++j){
			if(j > i-1)f[j] = seg.query(1,1,n,i-1,j-1) + c[j];
			else f[j] = c[j];
			for(int x : e[j])if(l[x] > 1)seg.modify(1,1,n,1,l[x]-1,w[x]);
		}
		ans = min(ans,f[n]);
	}
	printf("%d",ans);
	return 0;
}
posted @   Luzexxi  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示