首页 > 其他分享 >P7561 [JOISC 2021 Day2] 道路の建設案 (Road Construction)

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

时间:2024-08-13 20:07:40浏览次数:9  
标签:建設案 const Day2 距离 i64 JOISC ans multiset dis

这篇文章主要讲一下问什么要二分以后还要 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

相关文章

  • [JOISC2017] Cultivation
    link。不是,怎么四方跑得飞快啊?成最优解了?有人会卡吗?鉴定为剪枝太多导致的。一个出发点不太一样的思路。假设上下左右各被操作了\(U,D,L,R\)次。我们考虑一个点\((x,y)\)不被感染的条件是初始时\([x-D,x+U]\times[y-R,y+L]\)这个矩形内没有任何感染点。考虑扣出所有中间......
  • Day28 贪心算法part2
    任务122.买卖股票的最佳时机II给你一个整数数组prices,其中prices[i]表示某支股票第i天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。返回你能获得的最大利润。思路考虑分解最终......
  • 代码随想录day28 || 122 买卖最佳时机2,55 跳跃游戏,45 跳跃游戏2,1005 k次取反最大数组
    122买卖股票最佳时机2funcmaxProfit(prices[]int)int{ //思路,因为支持同一天买入卖出,所以利润最大应该是所有正利润的加总结果 varsumint fori:=1;i<len(prices);i++{ ifprices[i]-prices[i-1]>0{ sum+=prices[i]-prices[i-1] } } returns......
  • P9520 [JOISC2022] 监狱
    P9520[JOISC2022]监狱题目描述有一棵\(N\)个节点的树,有\(M\)个囚犯,要从\(S_i\)走到\(T_i\)。每一时刻可以发布一个命令让一名囚犯走到相邻的节点,要求任意时刻囚犯不能走到同一个节点上,求是否可以令每一个囚犯从\(S_i\)走到\(T_i\)。做法解析首先我们可以发现一个......
  • 日撸Java三百行(day20:小结)
    目录前言一、面向对象和面向过程相比,有哪些优势?1.封装2.继承3.多态4.协作5.组织结构二、比较顺序表和链表的异同1.相同点2.不同点2.1物理存储结构2.2查找2.3插入和删除三、分析顺序表和链表的优缺点1.顺序表1.1顺序表的优点1.2顺序表的缺点2.链表2.1链表的......
  • Day27 贪心算法part1
    任务455.分发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸s[j]。如果s[j]>=g[i],我们可以将这个饼干j分配给孩子i,这......
  • 代码随想录day27 || 455 分饼干,376 摆动序列,53 最大子序列和
    分饼干funcfindContentChildren(g[]int,s[]int)int{ //第一思路,双指针暴力解法 varcountint varused2=make([]bool,len(s)) g=quicksort(g) s=quicksort(s) for_,child:=rangeg{ foridx,cookie:=ranges{ if!used2[idx]&&cookie>=......
  • 【读书笔记-《30天自制操作系统》-1】Day1~Day2
    顾名思义,本书将制作操作系统的整个过程分成了30天来依次讲解。但其实每一天的内容多少与难度各不相同,也并不是每天就可以学习完书中一天的内容。前面的内容要少一些,也比较基础,因此先把第一天和第二天的内容合并起来整理。1.二进制与CPU作者没有从概念开始讲起,而是开篇就......
  • HDU-ACM 2024 Day2
    T1004a*bproblem(HDU7448)不会。T1005小塔的养成游戏之梦(HDU7449)不会。T1009强攻计策(HDU7453)容易发现初始速度是多少对答案没有影响,所以我们默认初始速度为\(0\)。题意相当于在平面直角坐标系上(横轴为时间,纵轴为速度),有一个目标高度,维护一条尽量接近目标的直线,但......
  • 算法笔记|Day22回溯算法IV
    算法笔记|Day22回溯算法IV☆☆☆☆☆leetcode491.递增子序列题目分析代码☆☆☆☆☆leetcode46.全排列题目分析代码☆☆☆☆☆leetcode47.全排列II题目分析代码☆☆☆☆☆leetcode332.重新安排行程(待补充)题目分析代码☆☆☆☆☆leetcode51.N皇后(待补充)题目分析......