欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF797F Mice and Holes 题解

Description

有一天 Masha 回到家,发现有 n 只老鼠在它公寓的走廊上,她大声呼叫,所以老鼠们都跑进了走廊的洞中。

这个走廊可以用一个数轴来表示,上面有 n 只老鼠和 m 个老鼠洞。第 i 只老鼠有一个坐标 xi ,第 j 个洞有一个坐标 yj 和容量 cj 。容量表示最多能容纳的老鼠数量。

找到让老鼠们全部都进洞的方式,使得所有老鼠运动距离总和最小。老鼠 i 进入洞 j 的运动距离 为 |xiyj|

无解输出 1

n,m106

Solution

首先直接贪心就考虑每只老鼠选离它左边最近的或右边最近的钻,但是这样不是全局最优的。

考虑反悔贪心。

先把老鼠和洞放到一起按坐标从小到大排序,然后从前往后扫。设 lstxi 表示第 i 只老鼠当前的最优答案,lstyi 表示第 i 个洞当前的最优答案。然后用两个堆分别维护老鼠和洞的信息。

如果当前枚举到了第 i 只老鼠,那么就要选一个洞匹配,设其为 j,那么匹配 (i,j) 对答案的贡献就是 (xiyj)lstyj=xi(yj+lstyj),于是只要用洞堆维护最大的 yj+lstyj,然后把答案加上 xi(yj+lstyj),同时 lstxixilstyj。由于当前老鼠还要反悔,所以把 xi+lstxi 放到老鼠堆里面。

如果枚举到了第 i 个洞是一样的,如果选了 j 号老鼠,贡献就是 yi(xj+lstxj),用老鼠堆维护最大的 xj+lstxj 即可,然后把答案加上 yi(xj+lstxj),同时 lstyiyilstxj

至于如何处理有一个坐标有很多个洞的问题,就每次只往堆里面放一个洞,如果这个匹配了就把匹配的放进去,然后如果还有洞就再选一个没匹配的洞放进去。

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

Code

#include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 1e6 + 5, kInf = 1e16;
int n, m, mm;
int x[kMaxN];
std::pair<int, int> h[kMaxN], a[kMaxN];
std::priority_queue<int> qm;
std::priority_queue<std::pair<int, int>> qh;
void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i <= n; ++i) {
std::cin >> x[i];
a[++mm] = {x[i], 0};
}
int sumc = 0;
for (int i = 1; i <= m; ++i) {
std::cin >> h[i].first >> h[i].second;
a[++mm] = {h[i].first, i};
sumc += h[i].second;
}
if (sumc < n) {
std::cout << "-1\n";
return;
}
std::sort(a + 1, a + 1 + mm);
int ans = 0;
for (int i = 1; i <= mm; ++i) {
if (!a[i].second) { // 老鼠
int val = kInf;
if (!qh.empty()) {
auto [p, id] = qh.top();
qh.pop();
val = a[i].first - p;
if (h[id].second) --h[id].second, qh.emplace(h[id].first, id);
}
ans += val;
qm.emplace(a[i].first + val);
} else { // 洞
int val;
while (!qm.empty() && h[a[i].second].second && (val = a[i].first - qm.top()) < 0) {
--h[a[i].second].second;
ans += val;
qm.pop();
qh.emplace(a[i].first + val, 0);
}
if (h[a[i].second].second) --h[a[i].second].second, qh.emplace(a[i].first, a[i].second);
}
}
std::cout << ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::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;
}
posted @   下蛋爷  阅读(94)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起