【Gym - 102174D】碟中谍(二分+变换主体+并查集)

Gym - 102174D

题意

有一条走廊可以视为二维平面上由 y = 0 y=0 y=0 y = w y=w y=w两条直线所框定的白色区域,中间有n个的报警传感器位于墙壁上或者走廊中。伊森在模型中可以视为具有一定半径的圆,位于走廊的最左侧 x = − ∞ x=−∞ x=,想穿过走廊到达最右侧 x = + ∞ x=+∞ x=+。求该圆可以具有的最大半径。
n ≤ 1000 , 0 ≤ w ≤ 1 0 5 , − 1 0 5 ≤ x , y ≤ 1 0 5 n \le 1000,0 \le w \le 10^5, -10^5 \le x,y \le10^5 n10000w105,105x,y105
在这里插入图片描述

题解

显然可以二分答案,问题在于如何验证答案。

  • 假设二分的圆的半径为R,如果直接判断该圆通过走廊,那么验证是比较困难的;
  • 换一个思路,如果将所有传感器的半径增加R(同时走廊间距减小2R),判断一个点能否通过走廊,就比较容易了。

此时只需用并查集维护一下连接起来的传感器的集合,每个集合维护一下上界和下界,判断一下是否有将路堵死的情况即可。每次判断复杂度为 O ( n 2 α ) O(n^2\alpha) O(n2α),总复杂度为 O ( T ∗ α n 2 l o g 2 ( w ) ) O(T*\alpha n^2log_2(w)) O(Tαn2log2(w))

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1200;

int x[N],y[N],r[N],n,w;
double dx[N][N];
int f[N];
double u[N],d[N];

int ff(int x){
	if(f[x]==x)return x;
	return f[x]=ff(f[x]);
}
bool check(double rx){
	for(int i=0;i<n;i++){
		u[i]=y[i]+r[i]+rx;
		d[i]=y[i]-r[i]-rx;
		f[i]=i;
		for(int j=0;j<i;j++){
			if(r[i]+r[j]+rx*2>dx[i][j]){
				int fi=ff(i),fj=ff(j);
				f[fi]=fj;
				u[fj]=max(u[fj],u[fi]);
				d[fj]=min(d[fj],d[fi]);
			}
		}
	}
	for(int i=0;i<n;i++)
		if(u[i]>w-rx && d[i]<rx)return 0;
	return 1;
}

int main(){
	ios::sync_with_stdio(0);
	int T;
	cin>>T;
	while(T--){
		cin>>w>>n;
		for(int i=0;i<n;i++){
			cin>>x[i]>>y[i]>>r[i];
			for(int j=0;j<i;j++)
				dx[i][j]=sqrt(ll(x[i]-x[j])*(x[i]-x[j])+ll(y[i]-y[j])*(y[i]-y[j]));
		}
		double l=0,r=w/2.0;
		while(r-l>=1e-8){
			double m=(l+r)/2;
			if(check(m))l=m;
			else r=m;
		}
		printf("%.8f\n",r);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值