Luogu1783 海滩防御

原题链接:https://www.luogu.com.cn/problem/P1783

海滩防御

题目描述

WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和仓库总是被敌方派人偷袭。于是,WLP动用了他那丰满且充实的大脑(或许更偏向前者),想出了一个好主意,他把海滩分成垂直于海岸线的若干列,在其中的几列上放置几个信号塔,试图来监视整个海滩。然而,WLP是一个非常心急的人,他把信号塔建好后才发现还需给信号塔供能,它们才能投入使用(这不是废话么),它们都有一个工作半径,一个圆形区域里的所有敌人都逃不过它们的监视,不过,WLP发现,敌人们非常狡猾,除非他将道路完全封死,否则WLP的敌人可以走过一条任意弯曲的路(不一定走整点,但是不会出第0列和第N列构成的边界)来偷他的东西。

于是,WLP就思考了:到底需要给每个信号塔多大的工作半径,才能将从海滩到内地的路径完全封死呢?他再次动用了他那丰满且充实的大脑,想了一堂数学课,终于,还是没想出来。于是,他向LZZ神犇求助(额……C_SUNSHINE的身份是不是暴露了)。

终于,在WLP:“%!*@#!*(*!*#@$^&(此处省略无数卖萌场景)”的哀求下,LZZ神犇写了一个程序,在1s内就解决了问题。但是,邪恶的LZZ神犇决定要将这个难题共享给无数无辜的OIer,所以,现在轮到你了。

输入格式

第一行两个整数 N N N M M M:表示海滩被WLP分成的列数0-N和信号塔个数。

第2-M+1行:每行两个数Xi,Yi表示1-M号信号塔所在的列数和离开海滩的距离。

输出格式

一行一个实数,表示最小的工作半径,保留两位小数。

输入输出样例
输入 #1

5 5
1 5
3 5
5 5
4 30
2 15

输出 #1

1.00

输入 #2

100 2
30 50
90 100

输出 #2

39.05

说明/提示

对于10%的数据:1≤M≤10,1≤Yi≤100;

对于30%的数据:1≤M≤50,1≤Yi≤1,000;

对于80%的数据:1≤M≤500,1≤Yi≤1,000;

对于100%的数据:1≤M≤800,1≤N≤1000,1≤Xi≤N,1≤Yi≤100,000.

【样例解释】

注意,封锁海滩是指,敌人的深入程度是有限制的,若敌人绕过了所有的信号塔,并且可以长驱直入,那么就说明道路没有完全封锁。

题解

二分答案+并查集

二分最小的半径,每次检查 r r r时将探测范围重叠(即距离 ≤ 2 r \le 2r 2r)的两座塔合并在同一并查集内,最后检查一下 0 0 0 n n n这两条边是否能通过中间的连通块连接在一起。

代码

最开始实现的时候我犯了个错误,我在判断两座塔联通后直接将两座塔所在的列放在了一个并查集里。这样等于将同一列的塔直接连接在了一起,在每列只有一座塔时没有错误,但是在一列有多座塔时就会导致我们默认将一列的塔连在了一个并查集里,当这两座塔的距离大于 2 r 2r 2r时就会出错。

#include<bits/stdc++.h>
# define L long long
using namespace std;
const int M=1005;
int n,m,dad[M];
double mid,le,ri,dis[M][M];
struct loc{
    
    int x,y;}twl[M];
void in()
{
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)scanf("%d%d",&twl[i].x,&twl[i].y);
}
double sqr(int x){
    
    return 1.0*x*x;}
int root(int v)
{
    
    
	int f=v;
	for(;dad[f]!=f;f=dad[f]);
	for(;dad[v]!=v;v=dad[v])dad[v]=f;
	return f;
}
bool check(double r)
{
    
    
	for(int i=1;i<=m;++i)dad[i]=i;
	for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)if(r>=dis[i][j]&&root(i)!=root(j))dad[root(i)]=root(j);
	for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)
	if(r>=twl[i].x&&r>=n-twl[j].x&&root(i)==root(j))return 1;
	return 0;
}
void ac()
{
    
    
	for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)
	dis[i][j]=sqrt(sqr(twl[i].x-twl[j].x)+sqr(twl[i].y-twl[j].y))/2,ri=max(ri,dis[i][j]);
	for(mid=(le+ri)/2;ri-le>1e-4;mid=(le+ri)/2)check(mid)?ri=mid:le=mid;
	printf("%.2lf",mid);
}
int main(){
    
    in(),ac();}

猜你喜欢

转载自blog.csdn.net/ShadyPi/article/details/109211610
今日推荐