[二分][并查集] Jzoj P5904 刺客信条(AC)

Description

          故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。
        这次的故事就是他暗杀一位作恶多端的红衣主教。红衣主教可以吸取他周围人的生命力量,而他的红衣教徒也拥有这个力量。红衣主教的家是一个x*y 的长方形房间,也就是说,他的家的四个角坐标分别为(0,0)(x,0)(0,y)(x,y)。教堂的门在(0,0) ,而红衣主教就在 (x,y)的卧室休息。他的家中还有n个守护着他的红衣教徒,站在(ai,bi)。Ezio想要趁主教休息时,从门进入潜入到他的卧室刺杀他,因为主教休息时会脱下红衣,这样吸取生命的力量就消失了。可是守卫他的红衣教徒依然很危险,离红衣教徒太近就会被吸取生命。因此,Ezio想知道,在能刺杀主教的前提,从门到他的卧室的路上,他最远和离他最近的红衣教徒保持多远的距离。注意:教徒都在房间里。
 

Input

第一行三个整数x,y,n。之后n行,每行两个整数ai,bi ,意义见题目描述。

Output

一行一个数D,表示Ezio能保持的最大距离,保留两位小数。
 

Sample Input

10 20 2
3 3
6 14

Sample Output

3.00



样例说明
贴着墙走
 

Data Constraint

数据范围
对 10%的数据n<=10,
 对 30%的数据n<=100
对 100%的数据n<=2000
保证输入合法,x,y属于[1,10^6].

 

 

题解

  • 如果,换个思路想想,我们把可以相通的点合并,那么最后只用判断能否有一条路经过
  • 然后二分一个长度,以每个点为圆心画圆,将不能联通的放在同一个并查集内
  • 那么如果左边的墙和下面的墙在同一个并查集内的话,那么就是不可能过去的嘛
  • 还有就是上和下,下和右,上和左某一组在同一个并查集内的话,就无法从(0,0)到(x,y),感性理解一下

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 using namespace std;
 5 const double eps=1e-4;
 6 int n,fa[2010];
 7 double l,r,ans,a[2010],b[2010],x,y;
 8 int getfather(int x) { return !fa[x]?x:fa[x]=getfather(fa[x]); }
 9 double sqr(double x) { return x*x; }
10 void merge(int x,int y)
11 {
12     int u=getfather(x),v=getfather(y);
13     if (u!=v) fa[u]=v;
14 }
15 bool check(double d)
16 {
17     memset(fa,0,sizeof(fa));
18     for (int i=1;i<=n;i++)
19     {
20         if (a[i]<=d) merge(i,n+1);
21         if (b[i]<=d) merge(i,n+2);
22         if (a[i]+d>=x) merge(i,n+3);
23         if (b[i]+d>=y) merge(i,n+4);
24     }
25     for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) if (sqr(a[i]-a[j])+sqr(b[i]-b[j])<=4*d*d) merge(i,j);
26     if (getfather(n+1)==getfather(n+2)) return false;if (getfather(n+1)==getfather(n+3)) return false;
27     if (getfather(n+2)==getfather(n+4)) return false;if (getfather(n+3)==getfather(n+4)) return false;
28     return true;
29 }
30 int main() 
31 {
32     freopen("AC.in","r",stdin),freopen("AC.out","w",stdout);
33     scanf("%lf%lf%d",&x,&y,&n);
34     for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i],&b[i]);
35     l=0,r=1e6;
36     while (r-l>=eps)
37     {
38         double mid=(l+r)/2;
39         if (check(mid)) ans=mid,l=mid+eps; else r=mid;
40     }
41     printf("%.2lf",ans);
42 }

 

posted @ 2018-10-15 19:52  BEYang_Z  阅读(227)  评论(0编辑  收藏  举报