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

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

时间:2023-07-07 12:22:05浏览次数:45  
标签:建設案 return int 题解 Day2 mid ans multiset check

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

题目描述

JOI 国是一个 \(x\times y\) 的二维平面,王国里有 \(n\) 个城镇,分别编号为 \(1, 2, \cdots, n \in [1,2.5 \times 10^5]\),其中第 \(i\) 个城镇的 坐标 为 \((x_i, y_i)\)。

在 JOI 国,正计划修建连接两座城镇的路(下文简称:「修路的项目」),路有 \(k\) 条。连接两个不同的城镇 \(a\) 和 \(b\) 将花费 \(|x_a − x_b| + |y_a − y_b|\) 元。若有一条连接 \(c\),\(d\) 的路,则不需要也不可以在建一条连接 \(d\),\(c\) 的路,因为它们是相同的。

你要管理这个「修路的项目」,为了计算花费情况,你得弄明白连接一些城镇所需的花费。在这 \(\dfrac{n\cdot(n-1)}{2}\) 条道路中,你想了解最便宜的 \(k\) 条道路的花费。

给你城镇的坐标以及 \(k\),请计算最便宜的 \(k\) 条路所需要的钱。

解析

首先你要知道什么是曼哈顿距离和切比雪夫距离及相互转化。

推荐学习曼哈顿距离与切比雪夫距离的互化

现在我把点坐标转化后就是要求解这样一个问题:

\[\max(|x_i-x_j|,|y_i-y_j|) \]

选出前 \(k\) 小。

我们可以二分一下第 \(k\) 小的值 \(mid\),再 check 有没有 \(k\) 个距离小于 mid

同时我们不需要真正数有多少点对,点对数 \(≥k\) 时直接返回 true 即可。

具体操作是先按照 \(x\) 排序。

假设当前到了 \(x_i\) ,将所有 \([x_i-mid,x_i]\) 的点放入 set 里面,

之前在 \([x_{i-1}-mid,x_{i-1}]\) 里面的但不在 \([x_i-mid,x_i]\) 删掉(用双指针维护)。

然后在 set 里面先二分找到 \(y_i-mid\),枚举到 \(y_i + mid\) ,每枚举到一个就 ++ans

ans >= k 的时候就直接 return

最后如何求出答案?

找到第 \(k\) 小的值 \(mid\) 了之后,再 check 一次 \(mid-1\),这样一定可以找到小于 \(k\) 个长度小于 \(mid\) 的长度,最后再用 \(mid\) 把 \(k\) 补满,就是答案。

因为 \(ans >= k\) 会 return 所以严格 \(O(n\log n)\)。

温馨提示

1.因为转化为切比雪夫距离,又要互相加减,所以要开 long long

2.用 multiset ,因为可能有相同长度。

代码

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 3e7 + 10; 
int n, k;
ll x,y,an[N],ans = 0,l,r,mid;
queue<int>q;
struct node{
	ll x, y;
	bool operator <(const node a)const{
		return x < a.x; 
	} 
}p[N];
struct Node{
	ll x, y;
	bool operator <(const Node a)const{
		return y < a.y; 
	} 
};
multiset<Node>s;
void input(){
	cin>>n>>k;
	for(int i = 1; i <= n; ++i){
		cin>>x>>y;
		p[i].x = x - y;
		p[i].y = x + y;
	}
	sort(p + 1,p + 1 + n);
}
bool check(ll mid){
	q = queue<int>();
	s = multiset<Node>();
	ans = 0;
	for(int i = 1; i <= n; ++i){
		while(q.size() && p[i].x - p[q.front()].x > mid){
			multiset<Node>:: iterator w = s.find((Node){0,p[q.front()].y});
			s.erase(w);
			q.pop();
		}	
		multiset<Node>::iterator lz = s.lower_bound((Node){0,p[i].y - mid});
		while(lz != s.end() && (*lz).y <= p[i].y + mid){
			an[++ans] = max(abs((*lz).x - p[i].x),abs((*lz).y - p[i].y));
			if(ans >= k) return 1;
			++lz;
		}
		q.push(i);
		s.insert((Node){p[i].x,p[i].y}); 
	}
	return 0;
}
void op(){
	
	l = 0,r = 4e9,mid;
	while(l < r){
		mid = (l + r) >> 1;
		if(check(mid)){
			r = mid;
		}else{
			l = mid + 1;
		}
	} 
	check(l - 1);
}
void output(){
	sort(an + 1,an + 1 + ans);
	int i;
	for(i = 1; i <= ans; ++i){
		cout<<an[i]<<'\n';
	}
	for(; i <= k; ++i){
		cout<<l<<'\n';
	}
}
int main(){
	cin.tie(0)->sync_with_stdio(false);
	input();
	op();
	output();
	return 0;
}

标签:建設案,return,int,题解,Day2,mid,ans,multiset,check
From: https://www.cnblogs.com/hfjh/p/17534628.html

相关文章

  • AT_bcu30_2019_qual_a 题解
    思路纯模拟题,给定N和P后,定义一个计数器sum,重复N次输入,每输入一次就判断P也就是子弹的能量是否≥每面墙的厚度x,如果是,就用P减去x,sum增加1,表示穿过了一面墙,否则跳出循环,输出sum。代码#include<iostream>usingnamespacestd;intmain(){intn,p,sum=0,......
  • AT_nikkei2019ex_h 题解
    思路这是一道博弈题,最优策略是高桥的k一直是1,青木的k一直是0,可以保证拿走的硬币不超过剩下的硬币,这样每次两人都取完后拿走硬币的数量是8^1+8^0,结果是9,那么就用Nmod9,得出的结果就是剩下的硬币。如果结果是0,那么最后拿的就是青木,输出Lose。如果结果是2、4、6,都......
  • 【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(三)
    ​贴接上回。。。 【往期FAQ参考】【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(一)【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(二) 【本期FAQ】1、第一次调用geolocation.getCurrentLocation()接口,弹出权限弹框后并未返回结果,再次调用接口才会成功返回?(API8......
  • AT_nikkei2019ex_e 题解
    思路进题扫一眼题目描述,可以写成这样:是不是很眼熟?这不就是角谷猜想嘛,但它不是让我们求步数果,而是求结果。它给了步数,求结果那就要倒推,第二个样例给了P最大时,也就是1000输出的结果1789997546303,我们就用这个结果往前推,带入角谷猜想的运算过程,就可以了。记得开longlong。......
  • AT_pakencamp_2020_day1_c 题解
    思路看到题目的第一句话我就知道要用map了。一道map的入门题,定义一个map来输入和统计参加次数后,定义一个计数器sum用来统计人数。代码#include<iostream>#include<string>#include<map>usingnamespacestd;map<string,int>personnel;intmain(){intn,sum......
  • CF1451F 题解
    problem&blog。这题原本的题解满是废话,让我写一篇(这边直接给结论了。令\(val_p=\oplus_{x+y=p}\a_{x,y}\),设\(S=\Big[\normalsize\forallval_i=0\Big]\),当\(S=\text{true}\)时,后手必胜;否则先手必胜。证明也是典中典。证两个条件即可。证明\(\forallS\Rightarr......
  • 记一次重装windows系统后笔记本键盘不能用的问题解决
    刚买了一台笔记本,预装的是Windows11。这个系统我见识过,优点还没看到,不习惯的地方很多。所以重装了Windows10LTSC。结果装完笔记本键盘不能用。这个情况之前用拯救者Y7000装plex的时候也遇到过,那时候没解决,这次非处理好不可下载驱动管理软件看,没有显示有对应键盘的驱动进设备管......
  • DAY2
    安装JDK浏览器输入www.oracle.com官网,点击product找到Java点击downloadJava,并且找到java8,并选择和自己电脑相匹配的版本注册Oracle账户以上步骤就是最基本的,剩余的安装步骤参考https://www.bilibili.com/video/BV1zr4y1F7ow/?buvid=XYF3637F627600F333D249B7CAB65......
  • 题解-Codeforces Round 805 (Div. 3) E. Split Into Two Sets
    题解-CodeforcesRound805(Div.3)E.SplitIntoTwoSets(原题链接)[Problem-E-Codeforces]思路知识点:种类并查集网上关于种类并查集的教学已经很多,在此不赘述在理解种类并查集时,很多文章会提到“敌人”,“朋友”的概念。而在不同的题目中,互为“敌人”,“朋友”的两个......
  • CodeChef Cutting Plants难题题解
    STL-CodeChefCuttingPlants题解单调队列哦我要造福后人,因为题解太jb难找了题意:2个操作找一段l-r区间,取其<=最小值的任意高度,记为h将l-r变为h时间复杂度暂时归为n吧思路:(思路应该很容易跟上)最特殊的:L=R,来嘛我每个独自减那为什么会有-1呢涨不回去呗(bi>ai)现在关键在......