[ZJOI2010] 基站选址
题目
题目最刁钻也是最核心的条件 : “如果在距离第
首先暴力:
- 状态
表示在 建了第 个基站,目前的花费是多少 - 其中
可以 预处理
分析
时间复杂度瓶颈在于
我们以上转移的过程花费统计是从给定端点出发,而考虑到条件里的覆盖与否只与
我们化繁为简,这就变为了一个类似二维数点的东西。
重新回顾
- 当我们扫到
这一处基站时,应当从 这个区间的最优点转移过来,故数据结构上的每个位置 维护的是:上层 值加上 ,即暴力中的 - 接着,对于先前
的一系列点 ,在此之后的转移中:- 满足了
,那么对于小于 的位置,就要加入 的花费了。
- 满足了
具体实现:
-
处理出每个点的
,并跟据 排序,本人用vector
实现,邻接表亦可; -
部分- 将上一阶段的结果放入数据结构中
- 从左到右枚举设立基站的位置,先询问
的最优结果。遍历 的点,在 加上 的价值
-
细节处理,由于要保证后面未统计部分记入答案,不妨
++n,++k
最后直接统计f_n
的结果
数据结构区间加,区间查询,继承结果,故使用线段树。时间复杂度
反思:
- 数据结构优化
,我们可以将上一阶段 放入数据结构中,进行转移。 - 对于式子,我们应当化繁为简,找出如二维偏序等重要特征
贴上代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现