#扫描线,并查集,切比雪夫距离#洛谷 5193 [TJOI2012]炸弹

题目

在平面上有 \(n\) 个炸弹 \([1 \ldots n]\)

每个炸弹的爆炸范围是 \(|x-x_i|+|y-yi| \leq R\)

如果某个炸弹爆炸了,那么它将引燃它范围内的所有炸弹。

求出至少引燃多少炸弹才能使得所有炸弹都爆炸。


分析

如果把所有可能爆炸的边连起来那么就转换成求无向图的连通块个数,这个用并查集实现,
曼哈顿距离不好做,考虑把它转换成切比雪夫距离
但是建边可能有\(O(n^2)\)条,考虑优化建边,将所有点按\(x\)坐标排序,
那么对于每个点只要考虑对纵坐标的前驱后继点相连那么连通块的点都能够相连
\(map\)维护即可


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <map>
#define rr register
using namespace std;
const int N=100011;
struct rec{int x,y;}a[N];
int n,R,f[N],ans; map<int,int>uk;
map<int,int>::iterator it;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans; 
}
bool cmp(rec x,rec y){return x.x<y.x||(x.x==y.x&&x.y<y.y);}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void uni(int x,int y){
	rr int fa=getf(x),fb=getf(y);
	if (fa!=fb) f[fa]=fb,--ans;
}
signed main(){
	ans=n=iut(),R=iut();
	for (rr int i=1;i<=n;++i){
		rr int x=iut(),y=iut();
		a[i]=(rec){x+y,x-y},f[i]=i;
	}
	sort(a+1,a+1+n,cmp);
	for (rr int i=1,j=1;i<=n;++i){
		for (;a[j].x+R<a[i].x;++j)
		    if (uk[a[j].y]==j) uk.erase(a[j].y);
		it=uk.lower_bound(a[i].y);
		if (it!=uk.end()){
			if (a[i].y+R>=it->first) uni(it->second,i);	
		}
		if (it!=uk.begin()){
			--it;
			if (it->first+R>=a[i].y) uni(it->second,i);	
		}
		uk[a[i].y]=i;
	}
	return !printf("%d",ans);
} 
posted @ 2021-07-07 11:38  lemondinosaur  阅读(87)  评论(0编辑  收藏  举报