当前位置: 首页 > news >正文

洛谷-P14333 [JOI2021 预选赛 R2] 安全检查 / Safety Inspection

洛谷-P14333 [JOI2021 预选赛 R2] 安全检查 / Safety Inspection

tag: 二分答案,贪心

一条数轴,上面有 $ N $ 个设施,设施 \(i\) 的位置坐标为 \(A_i\)。对这些设施进行安全检查。

设施 \(i\) 有 $ B_i $ 个必须检查的项目,有 \(K\) 名工人可执行检查工作。

开始时,所有工人均位于坐标 \(0\) 处。检查开始后,每位工人每分钟可完成以下两个行动之一:

  • 移动到距离当前坐标 \(1\) 单位的位置。
  • 在当前坐标处的设施中,选择一个未检查的项目进行检查。

安全检查结束时,所有设施的所有检查项目必须至少由一名工人检查过。求出完成所有安全检查所需的最短时间。

$ 1 \le N \le 10^5 \(,\) 1 \le K \le 10^9 \(,\) 1 \le A_i,B_i \le 10^9 $。保证 \(A\) 递增。

首先显然可以二分 + 验证,那么问题就变为,如何验证在 \(x\) 分钟内,是否可以完成任务。

考虑贪心策略。观察样例,一种自然的想法是:一个人 \(A\) 先去后面,让另一个人 \(B\) 自己在当前位置干,并且 \(B\) 以后都不用去后面了,尽量让两个人同时在两个地方完成自己所在位置的任务。另外就是,工人是不会走回头路的,这样一定不优。

考虑计算所需的总时间,每个工作都需要被做,时间为 \(\sum_iB_i\),剩余的需要最小化的时间就是工人花费在路上的时间。

先考虑对每一个工人计算答案,其花费在路上的时间就是他最远走到的(也是最后检查的)设施的坐标。

简单手模样例 4,发现安排工人的关键在于最后,我们可以考虑从后往前安排。由于我们已经钦定了总时间 \(x\),不妨令第一个人到达了 \(A_i\),那么他还剩 \(x-A_i\) 的时间去检查项目。于是贪心地让他检查 最靠后的 \(x-A_i\) 个项目 即可,这样就尽量让其他人不用来后面了,可以使总时间达到最优。

然而这道题不能对每个人都这样安排,因为 \(K\) 太大了,我们考虑让多个人捆绑在一起进行工作。从后往前扫,如果需要做的工作 \(S\)\(B\) 的后缀和)大于当前派遣到 \(>A_i\) 位置的工人的总贡献 \(C\)\(x-A_j\) 之和,其中 \(j\) 为工人派往的位置),那么就新增 \(\lceil(S-C)/(x-A_i)\rceil\) 名工人到 \(A_i\)

具体见代码实现。时间复杂度 \(O(N\log\sum B_i)\)

#include <bits/stdc++.h>
#define f(i, a, b) for (int i = (a); i <= (b); ++i)
#define g(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;void solve() {int n, k; cin >> n >> k;vi a(n + 1), b(n + 1);f(i, 1, n) cin >> a[i]; f(i, 1, n) cin >> b[i];vl s(n + 1); s[n] = b[n]; g(i, n - 1, 1) s[i] = s[i + 1] + b[i];auto check = [&](ll T) -> bool {ll w = 0; // used workersll c = 0; // workers total contributiong(i, n, 1) {if (c < s[i]) {// need new workersll need = s[i] - c;ll contrib = T - a[i]; // contribution of a worker who arrives at a[i]if (contrib <= 0) return false;ll cnt = (need + contrib - 1) / contrib; // number of workers needed (ceiling)w += cnt;if (w > k) return false;c += cnt * contrib;}}return true;};ll l = a[n], r = a[n] + s[1], mid;while (l + 1 < r) {mid = l + r >> 1;if (check(mid)) r = mid;else l = mid;}cout << r << '\n';return;
}signed main() {cin.tie(0)->sync_with_stdio(false);int tt = 1;// cin >> tt;while (tt--) solve();return 0;
}
http://icebutterfly214.com/news/54240/

相关文章:

  • 2025半期游忌
  • 90%的OKR都写成了KPI?其实你缺的不是表格,而是教练
  • PyTorch 分布式训练底层原理与 DDP 实战指南
  • 文字识别系统
  • SpringSecurity 集成 CAS Client 处理单点登录 - Higurashi
  • 25.11.20 最长不升序列LNIS和最长升序列LIS
  • 程序员手记
  • 详细介绍:MyBatis 与 Spring Data JPA 核心对比:选型指南与最佳实践
  • FreeSWITCH使用mod_fail2ban模块来提升安全
  • CF2165 VP 记录
  • 完整教程:Spring Boot Actuator全解析
  • 深入解析:css 的 clip-path 属性,绘制气泡
  • 深入解析:医疗多模态共情推理与学习一体化网络Python实现(2025扩充版)
  • es的sql语句 有哪些限制
  • find linux 文件
  • atom linux
  • ArangoDB并发控制如何进行负载均衡
  • access数据库和oracle使用便捷度
  • ArangoDB 文档存储怎样删除
  • Alluxio与MySQL的集成方式有哪些
  • 详细介绍:Python机器学习---6.集成学习与随机森林
  • Nov 20
  • 哈希表封装myunordered_map以及set - 详解
  • 斐波那契数列1-90
  • 北京离婚律师推荐:聚焦婚姻纠纷解决的专业法律服务
  • 查看指定文件名文件进行拷贝并进行压缩
  • 16. Ingress
  • WPF MVVM实战系列教程(二、使用Visual Studio 创建Prism项目)
  • 2025年11月份工信部人才交流中心PostgreSQL能力认证证书
  • 一样的吗?就是Flink中的Lookup join和Temporal join 的语法