Loading

P7561 [JOISC 2021 Day2] 道路の建設案 (Road Construction)

这篇文章主要讲一下问什么要二分以后还要 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;
}
posted @ 2024-08-13 19:55  SunnyYuan  阅读(10)  评论(0编辑  收藏  举报