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

QOJ #5076. Prof. Pang and Ants 题解

Description

在庞教授的大房子边上,有一群包含 \(m\) 只蚂蚁的蚁群,居住在有 \(n\) 个洞口的洞穴里。 它们会外出寻找食物。食物在庞教授的大冰箱里,蚂蚁们试图从里面偷出食物来。

特别的, 一只蚂蚁需要 \(1\) 秒从任何洞口离开,并同样需要 \(1\) 秒从任何洞口进入洞穴。不同的洞口有不同的位置,对一个洞口来说,它与冰箱的距离以 \(a_i\) 表示,同样的,一只蚂蚁从冰箱偷出食物再到第 \(i\) 个洞口的时间也是 \(a_i\) 秒。由于蚂蚁们技术高超,从冰箱拿出食物不会消耗它们任何时间。

每只蚂蚁都必须且只能从冰箱偷一次食物。蚂蚁可以任意选择一个洞口出发并进入任何一个洞口,两个洞口可以不同。一个洞口在 \(1\) 秒内只能有一只蚂蚁进出。因为这个原因,有些蚂蚁在偷完食物后需要等待一段时间才能进入洞口。

所以,你作为庞教授的好朋友, 需要计算出蚂蚁们偷出食物的最短时间。时间的定义为至少存在一只蚂蚁在洞穴外的时间总长,正在进出洞口的蚂蚁不被看作在洞穴里。

\(\sum n\leq 5\times 10^5,m\leq 10^{14}\)

Solution

二分答案 \(T\)

首先对于洞口 \(i\),第 \(k\) 个出洞的蚂蚁到冰箱的时间是 \(a_i+k\),第 \(k\) 个入洞的蚂蚁从冰箱出发的时间是 \(a_i+T-k+1\)。两只蚂蚁能够匹配当且仅当第一只蚂蚁到冰箱的时间不超过第二只蚂蚁从冰箱出发的时间。

一定满足出洞的占用的是一段时间上的前缀,入洞的占用的是一段后缀,否则调整之后显然更优。

考虑把入洞的过程也看成从洞里走向冰箱,问题变为需要对每个洞选择两个前缀,\(\{a_i+1,a_i+2,\ldots,a_i+s_i\}\)\(\{a_i+1,a_i+2,\ldots,a_i+t_i\}\),分别表示入洞的蚂蚁到冰箱的时间以及出洞的蚂蚁到冰箱的时间,需要满足 \(s_i+t_i\leq T\)

现在对所有的前缀和后缀分别合并的结果需要找到一个大小为 \(m\) 的匹配,使得每个匹配的两个数加起来不超过 \(T\)

感性理解一下,前缀和后缀的集合一定尽量相同,否则看起来不太平衡?所以每个洞口一定满足入洞的占用不超过前一半,出洞的占用不超过后一半,这样就基本保证了不交这个限制。


后面的过程还是换回最开始的判定。

对于 \(T\) 是偶数的情况,把所有洞口 \(i\) 的前缀 \([a_i+1,a_i+T/2+1]\) 和后缀 \([T/2,T-a_i-1]\) 分别归并起来,从小到大贪心判断匹配数是否大于等于 \(m\) 即可。

如果 \(T\) 是奇数,则每个洞口在正中间的时间会有一点小细节。注意到类型相同的正中间时刻之间是无法匹配的,考虑把正中间的时刻拆成可以进出各 \(0.5\) 只蚂蚁,根据对称性,拆除来的两个小蚂蚁最终匹配的结果一定(?)也对称,可以看成一只中间的蚂蚁匹配两个对称蚂蚁中的一个。

时间复杂度:\(O(n\log n+n\log m)\)

Code

#include <bits/stdc++.h>#define int int64_tconst int kMaxN = 1e5 + 5;int n, m;
int a[kMaxN];template<class T>
void merge(std::vector<T> &v1, std::vector<T> &v2) {std::vector<T> ret;for (int i = 0, j = 0; i < v1.size() || j < v2.size();) {if (i < v1.size() && (j == v2.size() || v1[i] < v2[j])) ret.emplace_back(v1[i++]);else ret.emplace_back(v2[j++]);}v1.swap(ret);
}bool check(int t) {std::vector<std::tuple<int, int, int>> vec;if (t % 2 == 0) {std::vector<std::tuple<int, int, int>> vv[4];for (int i = 1; i <= n; ++i) vv[0].emplace_back(a[i] + 1, 0, 2);for (int i = 1; i <= n; ++i) vv[1].emplace_back(a[i] + t / 2 + 1, 0, -2);for (int i = n; i; --i) vv[2].emplace_back(t / 2 - a[i], 1, 2);for (int i = n; i; --i) vv[3].emplace_back(t - a[i], 1, -2);for (int i = 0; i < 4; ++i) merge(vec, vv[i]);} else {std::vector<std::tuple<int, int, int>> vv[6];for (int i = 1; i <= n; ++i) vv[0].emplace_back(a[i] + 1, 0, 2);for (int i = 1; i <= n; ++i) vv[1].emplace_back(a[i] + t / 2 + 1, 0, -1);for (int i = 1; i <= n; ++i) vv[2].emplace_back(a[i] + t / 2 + 2, 0, -1);for (int i = n; i; --i) vv[3].emplace_back(t / 2 - a[i], 1, 1);for (int i = n; i; --i) vv[4].emplace_back(t / 2 - a[i] + 1, 1, 1);for (int i = n; i; --i) vv[5].emplace_back(t - a[i], 1, -2);for (int i = 0; i < 6; ++i) merge(vec, vv[i]);}int cnt = 0, las = 0, now[2] = {0};for (int i = 0; i + 1 < (int)vec.size(); ++i) {if (i + 1 < (int)vec.size() && std::get<0>(vec[i]) == std::get<0>(vec[i + 1])) continue;int len = std::get<0>(vec[i + 1]) - std::get<0>(vec[i]);for (int j = i; ~j; --j) {if (std::get<0>(vec[j]) != std::get<0>(vec[i])) break;now[std::get<1>(vec[j])] += std::get<2>(vec[j]);}if (now[0] >= now[1]) {cnt += now[1] * len, las += (now[0] - now[1]) * len;} else {cnt += now[0] * len;int c1 = (now[1] - now[0]) * len;cnt += std::min(c1, las), las -= std::min(c1, las);}}return cnt >= 2 * m;
}void dickdreamer() {std::cin >> n >> m;for (int i = 1; i <= n; ++i) std::cin >> a[i];std::sort(a + 1, a + 1 + n);int L = 0, R = 2e14, res = 2e14;while (L + 1 < R) {int mid = (L + R) >> 1;if (check(mid)) R = res = mid;else L = mid;}std::cout << res << '\n';
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);int T = 1;std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}
http://www.hskmm.com/?act=detail&tid=15517

相关文章:

  • 发现5个宝藏文件摆渡系统 2025年企业首选的摆渡方案是这个!
  • 漏洞挖掘实战:如何定制化模糊测试技术
  • nuxt3中使用pdfjs-dist实现pdf转换canvas实现浏览
  • 查看linux部署网站的TLS版本号
  • 【SpringBoot- Spring】学习
  • 基于Python+Vue开发的摄影网上预约管理系统源码+运行步骤
  • 【习题答案】《深入理解计算机系统(原书第三版)》
  • 深入解析:mosquitto求医之路(3):Docker安装也不好使
  • 在K8S中,在服务上线的时候Pod起不来怎么进行排查?
  • 在线教育软件开发的全流程解析与优化方案
  • 在K8S中,⼀个pod的不同container能够分开被调动到不同的节点上吗?
  • 在K8S中,如果是因为开发写的镜像问题导致pod起不来该怎么排查?
  • 上海应用大学网课自动化学习脚本(基于Python selenium)代码重构为GUI界面 —— 技术笔记
  • 在K8S中,Deployment⽀持扩容吗?它与HPA有什么区别?
  • 开源语音识别FunASR入门详解
  • AT_abc201_f [ABC201F] Insertion Sort 题解
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • c语言动态内存分配
  • 2025.9.24——1橙
  • AT_arc172_d [ARC172D] Distance Ranking
  • Python爬虫实现大乐透历史数据抓取
  • 【读书笔记】《深入理解计算机系统(原书第三版)》第一章 计算机系统漫游
  • 如何将PPT每一页批量导出为高清JPG图片?一文讲清处理流程
  • 实用指南:计算机视觉:基于YOLOv11 实例分割与OpenCV 在 Java 中的实现图像实例分割
  • Java实现双色球历史是否中奖查询
  • ABC424 游记(VP)
  • Java实现大乐透历史是否中奖查询
  • 阿德勒的课题分离是很好用的东西
  • 别再混淆 PHP8.1 中纤程 Fibers 和协程 Coroutines 了 一文搞懂它们的区别
  • 主要测试的测试用例