P2605 [ZJOI2010] 基站选址

news/2025/10/16 20:38:16/文章来源:https://www.cnblogs.com/high-sky/p/19146578

题目概述

题目链接:https://www.luogu.com.cn/problem/P2605。

\(n\) 个村庄,你需要建立不超过 \(k\) 个基站,每一户人家都有参数 \(d_i,s_i,w_i,c_i\) 分别表示距离第一户人家的距离、在不超过 \(s_i\) 的地方有基站才能覆盖此地、没有被基站覆盖的补偿费用、在此建立基站的费用。

求最小费用。

分析

一道经典题目,来记录一下。

首先不难处理 \(l_i,r_i\) 表示在这个范围内的人家只要有建基站那么就能覆盖此地。

二分即可。

\(f_{i,j}\) 表示前 \(i\) 个人家建立 \(j\) 个基站的最小代价(当前也要建)。

转移是显然的:

\[f_{i,j}=\min_{l\in[1,i-1]} f_{l,j-1}+cost_{l+1,i-1}+c_i \]

直接搞是 \(\mathcal{O}(n^3)\) 的。

先考虑如何把 \(cost\) 算掉。

在我每一次循环 \(i\) 的时候,遍历计算 \(cost\) 即可。

那么就可以得到一个客观的 \(\mathcal{O}(n^2)\) 的代码,有 \(60pts\)

#include <iostream>
#include <stdlib.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 200005
#define M 105
using namespace std;
#define min(x,y) ((x)<(y)?(x):(y))
const int INF = 2e9;
int n,k,d[N],c[N],s[N],w[N],l[N],r[N];
int f[N][M],cost[N],ans;
signed main() {scanf("%d%d",&n,&k);for (int i = 2;i <= n;i ++) scanf("%d",&d[i]);for (int i = 1;i <= n;i ++) scanf("%d",&c[i]);for (int i = 1;i <= n;i ++) scanf("%d",&s[i]);for (int i = 1;i <= n;i ++) scanf("%d",&w[i]);for (int i = 1;i <= n;i ++) {l[i] = lower_bound(d + 1,d + 1 + n,d[i] - s[i]) - d;r[i] = lower_bound(d + 1,d + 1 + n,d[i] + s[i]) - d;r[i] -= (d[i] + s[i] < d[r[i]]);}for (int i = 1;i <= n;i ++) {for (int j = 1;j <= k;j ++) f[i][j] = INF;f[i][0] = f[i-1][0] + w[i];}ans = f[n][0];for (int i = 1;i <= n;i ++) {memset(cost,0,sizeof cost);for (int j = i - 1;j;j --)if (r[j] < i) cost[l[j] - 1] += w[j];for (int j = i - 1;j >= 0;j --) cost[j] += cost[j + 1];for (int j = 1;j <= k && j <= i;j ++) {if(j == 1) f[i][1] = cost[0] + c[i];elsefor(int p = i - 1;p >= j - 1;p --)f[i][j] = min(f[i][j],f[p][j - 1] + cost[p] + c[i]);int sum = 0;for (int p = i + 1;p <= n;p ++)if (l[p] > i) sum += w[p];ans = min(ans,f[i][j] + sum);} }printf("%d\n",ans);return 0;
}

考虑怎么优化。

显然,可以先枚举有多少个基站,这在斜率优化 \(dp\) 种颇有体现,以及 \(wqs\) 二分的处理方式也有。

可惜的是如果你这么改了之后,你的暴力只有 \(40pts\) 了。

#include <iostream>
#include <stdlib.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 200005
#define M 105
using namespace std;
#define min(x,y) ((x)<(y)?(x):(y))
const int INF = 2e9;
int n,k,d[N],c[N],s[N],w[N],l[N],r[N];
int f[N],cost[N],ans;
signed main() {scanf("%d%d",&n,&k);for (int i = 2;i <= n;i ++) scanf("%d",&d[i]);for (int i = 1;i <= n;i ++) scanf("%d",&c[i]);for (int i = 1;i <= n;i ++) scanf("%d",&s[i]);for (int i = 1;i <= n;i ++) scanf("%d",&w[i]);for (int i = 1;i <= n;i ++) {l[i] = lower_bound(d + 1,d + 1 + n,d[i] - s[i]) - d;r[i] = lower_bound(d + 1,d + 1 + n,d[i] + s[i]) - d;r[i] -= (d[i] + s[i] < d[r[i]]);}for (int i = 1;i <= n;i ++) f[i] = f[i - 1] + w[i];ans = f[n];// for (int i = 1;i <= n;i ++) {//     memset(cost,0,sizeof cost);//     for (int j = i - 1;j;j --)//         if (r[j] < i) cost[l[j] - 1] += w[j];//     for (int j = i - 1;j >= 0;j --) cost[j] += cost[j + 1];//     for (int j = 1;j <= k && j <= i;j ++) {//         if(j == 1) f[i][1] = cost[0] + c[i];//         else//             for(int p = i - 1;p >= j - 1;p --)//                 f[i][j] = min(f[i][j],f[p][j - 1] + cost[p] + c[i]);//         int sum = 0;//         for (int p = i + 1;p <= n;p ++)//             if (l[p] > i) sum += w[p];//         ans = min(ans,f[i][j] + sum);//     } // }for (int i = 1;i <= n;i ++) {memset(cost,0,sizeof cost);for (int j = i - 1;j;j --)if (r[j] < i) cost[l[j] - 1] += w[j];for (int j = i - 1;j >= 0;j --) cost[j] += cost[j + 1];f[i] = cost[0] + c[i];int sum = 0;for (int p = i + 1;p <= n;p ++)if (l[p] > i) sum += w[p];ans = min(ans,f[i] + sum);}for (int j = 2;j <= k;j ++) {for (int i = j;i <= n;i ++) {memset(cost,0,sizeof cost);for (int p = 1;p < i;p ++)if (r[p] < i) cost[l[p] - 1] += w[p];for (int p = i - 1;p >= 0;p --) cost[p] += cost[p + 1];for (int p = i - 1;p >= j - 1;p --)f[i] = min(f[i],f[p] + cost[p] + c[i]);int sum = 0;for (int p = i + 1;p <= n;p ++)if (l[p] > i) sum += w[p];ans = min(ans,f[i] + sum);}}printf("%d\n",ans);return 0;
}

不过确实少了不少限制。

主要的时间开销为计算 \(cost\) 以及我们的暴力转移,我们的 \(k\) 是省略不掉的。

我们现在的转移是:

\[f_i=\min_j f_j+c_i+cost_{j+1,i-1} \]

首先处理 \(k=1\)\(cost\)

考虑怎么快速转移。

我们发现限制我们 \(cost\) 的只有 \(r\),那么我们可以根据 \(i\) 的需要,去尺取变化就行了。

那你 \(k=1\) 都用 \(\mathcal{O}(n\log n)\) 解决了,那你再来个 \(k\),再来个线段树维护 \(dp\) 值加上前面的 \(cost\) 就行了。

代码

时间复杂度 \(\mathcal{O}(kn\log n)\)

#include <iostream>
#include <stdlib.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 200005
#define M 105
using namespace std;
const int INF = 2e9;
int min(int a,int b) {return a < b ? a : b;}
int n,k,d[N],c[N],s[N],w[N],l[N],r[N];
int f[N],cost[N],ans;
struct node{int r,id;
}ls[N];
int tr[N << 2],lz[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
void pushup(int x) {tr[x] = min(tr[ls(x)], tr[rs(x)]);
}
void pushdown(int x) {tr[ls(x)] += lz[x],tr[rs(x)] += lz[x];lz[ls(x)] += lz[x],lz[rs(x)] += lz[x];lz[x] = 0;
}
void build(int x,int l,int r) {lz[x] = 0;if (l == r) {tr[x] = f[l];return;}int mid = l + r >> 1;build(ls(x),l,mid),build(rs(x),mid + 1,r);pushup(x);
}
void update(int x,int l,int r,int L,int R,int val) {if (l > R || r < L) return ;if (L <= l && r <= R) {tr[x] += val;lz[x] += val;return;}if (lz[x]) pushdown(x);int mid = l + r >> 1;update(ls(x),l,mid,L,R,val),update(rs(x),mid + 1,r,L,R,val);pushup(x);
}
int query(int x,int l,int r,int L,int R) {if (l > R || r < L) return INF;if (L <= l && r <= R) return tr[x];if (lz[x]) pushdown(x);int mid = l + r >> 1;return min(query(ls(x),l,mid,L,R),query(rs(x),mid + 1,r,L,R));
}
signed main() {scanf("%d%d",&n,&k);for (int i = 2;i <= n;i ++) scanf("%d",&d[i]);for (int i = 1;i <= n;i ++) scanf("%d",&c[i]);for (int i = 1;i <= n;i ++) scanf("%d",&s[i]);for (int i = 1;i <= n;i ++) scanf("%d",&w[i]);for (int i = 1;i <= n;i ++) {l[i] = lower_bound(d + 1,d + 1 + n,d[i] - s[i]) - d;r[i] = lower_bound(d + 1,d + 1 + n,d[i] + s[i]) - d;r[i] -= (d[i] + s[i] < d[r[i]]);ls[i] = {r[i],i};}// for (int i = 1;i <= n;i ++) {//     memset(cost,0,sizeof cost);//     for (int j = i - 1;j;j --)//         if (r[j] < i) cost[l[j] - 1] += w[j];//     for (int j = i - 1;j >= 0;j --) cost[j] += cost[j + 1];//     for (int j = 1;j <= k && j <= i;j ++) {//         if(j == 1) f[i][1] = cost[0] + c[i];//         else//             for(int p = i - 1;p >= j - 1;p --)//                 f[i][j] = min(f[i][j],f[p][j - 1] + cost[p] + c[i]);//         int sum = 0;//         for (int p = i + 1;p <= n;p ++)//             if (l[p] > i) sum += w[p];//         ans = min(ans,f[i][j] + sum);//     } // }// for (int i = 1;i <= n;i ++) {//     memset(cost,0,sizeof cost);//     for (int j = i - 1;j;j --)//         if (r[j] < i) cost[l[j] - 1] += w[j];//     for (int j = i - 1;j >= 0;j --) cost[j] += cost[j + 1];//     f[i] = cost[0] + c[i];//     int sum = 0;//     for (int p = i + 1;p <= n;p ++)//         if (l[p] > i) sum += w[p];//     ans = min(ans,f[i] + sum);// }// for (int j = 2;j <= k;j ++) {//     for (int i = j;i <= n;i ++) {//         memset(cost,0,sizeof cost);//         for (int p = 1;p < i;p ++)//             if (r[p] < i) cost[l[p] - 1] += w[p];//         for (int p = i - 1;p >= 0;p --) cost[p] += cost[p + 1];//         for (int p = i - 1;p >= j - 1;p --)//             f[i] = min(f[i],f[p] + cost[p] + c[i]);//         int sum = 0;//         for (int p = i + 1;p <= n;p ++)//             if (l[p] > i) sum += w[p];//         ans = min(ans,f[i] + sum);//     }// }stable_sort(ls + 1,ls + 1 + n,[](node x,node y) {return x.r < y.r;});for (int i = 1;i <= n;i ++) ans += w[i];for (int i = n;i;i --) cost[l[i] - 1] += w[i];for (int i = n - 1;i;i --) cost[i] += cost[i + 1];int now = 0;for (int i = 1;i <= n;i ++) {f[i] = now + c[i];int it = lower_bound(ls + 1,ls + 1 + n,(node){i,0},[](node x,node y) {return x.r < y.r;}) - ls;while(ls[it].r == i && it <= n) {now += w[ls[it].id];it ++;}ans = min(ans,f[i] + cost[i]);}for (int j = 2;j <= k;j ++) {build(1,1,n);for (int i = j;i <= n;i ++) {f[i] = query(1,1,n,j - 1,i - 1) + c[i];ans = min(ans,f[i] + cost[i]);int it = lower_bound(ls + 1,ls + 1 + n,(node){i,0},[](node x,node y) {return x.r < y.r;}) - ls;while(ls[it].r == i && it <= n) {update(1,1,n,1,l[ls[it].id] - 1,w[ls[it].id]);it ++;}}}printf("%d\n",ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/938495.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NVIDIA Jetson AGX Xavier刷机教程

NVIDIA Jetson AGX Xavier刷机教程 @目录NVIDIA Jetson AGX Xavier刷机教程1.下载SdkManager2.安装SdkManager3.刷机 1.下载SdkManager 进入SdkManager官网下载SdkManager的deb版本 注意,ubuntu的24.04版本暂时不支持…

洛谷p1462-通往奥格瑞码道路

题目:https://www.luogu.com.cn/problem/P1462 思考过程: 刚拿到这个题,我把消耗的血量和路费当成了负权边去考虑,考虑到我们只需要考虑扣钱的最优,并且在血量扣尽之前达到就好,那我觉得可以用一个数组hp来代表实…

详细介绍:VR 太阳光参数与快速渲染

详细介绍:VR 太阳光参数与快速渲染pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco…

位运算中没用的小技巧

位运算那些没用的小技巧消去最后一位的1 x & (x - 1)交换两个数 a ^= b; b ^= a; a ^= b;检查一个数的奇偶 x & 1求x的绝对值 ~(x >> 31) ? a : ~a + 1取两个数的最大值 x ^ ((x ^ y) & -(x < y…

超越基础:SightAI 智能路由与多模型选择实战 - sight

在第一周的入门指南中,我们已经学会了如何使用 SightAI 的基础 API 调用,实现了一个简单的对话交互。不少开发者会面临新的需求:想降低推理成本时,如何找到性价比更高的模型?追求交互速度时,怎样获取更快的响应?…

kafka连接认证

kafka连接认证明白,我帮你整理一份 Kafka 客户端三阶段检查完整文档,涵盖原理、检查目的、可能异常、排查方法以及你之前脚本逻辑对应的解释。文档形式清晰,方便阅读和运维参考。Kafka 客户端三阶段连接检查完整文档…

[Vulhub靶机]JARBAS靶机渗透

[Vulhub靶机]JARBAS靶机渗透 靶机搭建 下载地址: https://download.vulnhub.com/jarbas/Jarbas.zip 下载后设置nat模式即可与攻击机同一网段 kali:192.168.88.1331.信息收集 主机发现 首先对C段进行扫描,发现131这台主…

CF622D 题解

很好的一道思维题。 容易发现答案下界是 \(0\),手模几组发现好像都能取到,考虑能不能找到构造答案为 \(0\) 的通解。 那么除了 \(i=n\) 以外的所有 \(i\) 都应该有 \(d_i=n-i\),考虑构造这个东西,然后最后 \(n\) 随…

vue学习的总结

vue学习如何判断vue框架: (1)使用插件(最无脑的) (2)判断是否有id为app的div (3)判断站点是否只有一个index.html (4)可以用findVueRoot函数 (5)使用油猴的脚本进行清除路由若能清除也可以判断为vue(油猴…

最小二乘问题详解5:非线性最小二乘求解实例

通过拟合非线性函数的实例,详细讲解了非线性最小二乘问题中Gauss-Newton方法的实现过程,并给出了基于Eigen库的C++代码示例及收敛分析。1. 引言 在上一篇文章《最小二乘问题详解4:非线性最小二乘》中,介绍了非线性…

【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、强大的方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】

【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、强大的方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto…

第五周预习

20231313 张景云《密码系统设计》第五周预习AI对内容的总结 Windows.C.C++.加密解密实战.sm.ys 一、数字签名技术基础背景与意义:互联网信息安全问题突出,如网站篡改、仿冒页面等,数字签名技术作为基于密码学的安全…

2025 非标门/铸铝门/别墅大门厂家推荐榜:聚焦品质与服务的实力之选

在建筑建材领域,门作为空间防护与装饰的核心载体,其品质直接关系到居住安全与生活体验。市面上门业厂商众多,产品性能与服务质量参差不齐。基于技术实力、产品适配性、市场口碑等多维度考量,本文筛选出五家各具优势…

工业数字化未来:IT与OT融合实践

本文探讨数字技术在工业与运营环境中的实际应用,包括远程诊断系统、统一登录平台和跨设备协作解决方案,分析如何通过优化数字员工体验提升生产力与安全性,并介绍多家机构在IT与OT融合过程中的实践经验。工业运营工作…

AI安全新威胁:提示注入与模型中毒攻击深度解析

本文深入探讨AI安全领域两大新兴威胁:提示注入与模型中毒攻击。详细分析直接注入、间接注入、多模态注入等攻击技术,以及数据投毒、后门植入等模型威胁,为企业安全团队提供全面的防御策略和实战案例。提示注入与模型…

神经网络入门研读报告

神经网络入门研读报告:基于数据驱动的机器认知模型 神经网络是一种模拟生物神经系统信息处理机制的机器学习模型,核心功能是通过多层非线性变换,从结构化或非结构化数据中自动学习特征与规律,实现分类、预测等认知…

阅读《记录一类分治方法》笔记

目前只有题一。阅读《记录一类分治方法》笔记 题一 \(n\) 个点的无向图,边有边权。\(q\) 次操作,每次操作是添加/删除一条边、修改一条边的边权、查询最小生成树这四种之一。 前言 其实只看了题一,后面都还没看呢。…

CF2140E2

给定 \(n(1 \le n\le 20), m(1 \le m \le 10^6)\) 和一些 \(p_i\)。有 \(n\) 堆石子,每堆石子有 \(1 \sim m\) 个。两个人进行博弈,每次每个人可以取走第 \(p_i\) 堆石子(\(i\) 任选),然后剩下的石堆重新编号,直…

Codeforces 380E Sereja and Dividing 题解 [ 紫 ] [ 线段树 ] [ 贪心 ] [ 数学 ]

Sereja and Dividing:一年前的模拟赛就能秒这个 *2600 了,可我现在怎么还这么菜 /ll/ll/ll。 先考虑当杯子的集合固定时如何选择,显然一个杯子不会被选第二次,并且杯子从大到小选一定是最优的。证明只需要列出最后…

JPA教程

一 什么是 Spring Data JPA Spring Data JPA 是 Spring 框架的一个子项目,它简化了基于 JPA (Java Persistence API) 的数据访问层开发。它通过提供一套抽象接口,减少了开发者编写重复性的数据访问代码的工作量,使开…