这篇文章主要讲一下问什么要二分以后还要 check(l - 1)
,以及怎么找距离小于等于 \(k\) 的边的数量。
题目
给定 \(n\) 个点,求出任意两个点的曼哈顿距离的集合的前 \(k\) 大。
思路
我们先将曼哈顿距离转化为切比雪夫距离:我们知道形如 \((x, y)\) 点之间的曼哈顿距离等于 \((x + y, x - y)\) 点之间的切比雪夫距离。
转化为切比雪夫距离之后,怎么求前 \(k\) 大又是个问题。
我们可以二分一个 \(dis\),看看小于等于 \(dis\) 距离的数量是否大于等于 \(k\),如果大于等于 \(k\),那么可以缩小 \(dis\),如果小于 \(k\),我们将 \(dis\) 调大。
那么二分怎么 check 呢?
假设现在 check 的是距离 dis。
我们可以先将所有坐标按照横坐标从小到大的顺序进行排序。
定义一个队列 queue 从前往后录入点;一个多重集 multiset 将 queue 中的点进行排序,按照纵坐标 \(y\) 进行排序。
队列和集合维护的点的集合是相同的,不会出现一个点在 queue 中,不在 multiset 中,所以增加数字的时候一起加,删除数字的时候一起删。
我们从 \(1\) 到 \(n\) 枚举每一个点,首先第 \(i\) 个点要进队列,所有点的横坐标 \(x\) 小于 \(x_i - dis\) 的点的距离都与 \(i\) 保持了超过 \(dis\) 的距离,所以要将他们从队尾弹出并从集合中删除;因为是切比雪夫距离,我们已经保证了横坐标不会超过 \(dis\),接下来就看纵坐标,所有 \(y_i - dis \le y_x \le y_i + dis\) 的点的距离都小于等于 \(dis\),可以采纳,这一步我们可以使用 multiset 中的 lower_bound
函数实现,找到第一个纵坐标大于等于 \(y_i - dis\) 的点,然后一直找这个点的后继(平衡树的后继,在 multiset 中直接将指针 +1 即可,非常方便),直到超过 \(y_i + dis\),将这些点的距离放入答案数组 \(ans\),即 \(ans\) 数组新增 \(\max(x_i - x_p, |y_i - y_p|)\),\(x\) 不用写绝对值,因为已经排过序了;如果 \(ans\) 数组记录的答案个数已经超过 \(k\),立即返回成功(true),不能再记下去了;最后我们将点 \((x_i, y_i)\) 放入 queue 的队头和 multiset,必须最后放入,因为不能将自己和自己的距离(0)统计进去。
二分完以后我们还要再 check(l - 1)
一下,因为 \(dis = l\),会导致个数大于等于 \(k\),但是我们刚刚超过 \(k\) 个时直接返回了,所以后面还有小于 \(dis\) 距离没有统计到,我们又不是从小到大将距离添加到 \(ans\) 的,所以我们退而求其次,将所有 \(dis = l - 1\) 的部分求出来后,其一定小于 \(k\) 个,再在 \(ans\) 的数组后面添加距离 \(l\),将其补齐到 \(k\) 个,最后将 \(ans\) 排序并输出。
时间复杂度:\(O(n \log^2 n)\)。
代码
注意 multiset 必须先放入极大值和极小值。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 250010;
const i64 INF = 1e18;
struct node {
i64 x, y;
} p[N];
struct cmp {
bool operator()(const node& a, const node& b) const {
return a.y < b.y;
}
};
int n, k;
i64 ans[N], cnt;
bool check(i64 dis) {
multiset<node, cmp> mp;
queue<int> q;
mp.insert({INF, INF});
mp.insert({-INF, -INF});
cnt = 0;
for (int i = 1; i <= n; i++) {
while (q.size() && p[q.front()].x < p[i].x - dis) {
mp.erase(mp.find({p[q.front()].x, p[q.front()].y}));
q.pop();
}
auto pos = mp.lower_bound({INF, p[i].y - dis});
while ((pos->y) <= p[i].y + dis) {
ans[++cnt] = max(p[i].x - (pos->x), abs(p[i].y - (pos->y)));
if (cnt >= k) return true;
pos++;
}
q.push(i);
mp.insert({p[i].x, p[i].y});
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
i64 a, b;
cin >> a >> b;
p[i].x = a + b, p[i].y = a - b;
}
sort(p + 1, p + n + 1, [](const node& a, const node& b) { return (a.x < b.x) || ((a.x == b.x) && (a.y < b.y)); });
i64 l = 1, r = INF;
while (l < r) {
i64 mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
check(l - 1);
sort(ans + 1, ans + cnt + 1);
for (int i = 1; i <= cnt; i++) cout << ans[i] << '\n';
for (int i = cnt + 1; i <= k; i++) cout << l << '\n';
return 0;
}
标签:建設案,const,Day2,距离,i64,JOISC,ans,multiset,dis
From: https://www.cnblogs.com/Yuan-Jiawei/p/18357571