$$AVICII$$

#Star Way To Heaven 优化二分 /prim<凉宫春日的忧郁>

之前是贪心看不粗来,现在二分也看不粗来了,QAQ。。。

审题清楚,别没看懂题就忙着暴搜,小w不一定走整点,可以走直线

其实,看到就应该想到二分了

那么其实二分的答案是作为每个点的半径以圆的形式出现的,一个答案是否成立,看这些障碍圆建出来后,是否还能从左到右联通,那其实是看障碍组成的边界线是否能联通上下的边界。并查集,dfs都可维护。然后是T80的好成绩。

考虑优化,每次并查集会枚举所有点判距离(毕竟6000^2开不下),这其中是有冗余状态的,考虑这种情况,a可以和他最近的b相连,b可以和他最近的c相连,a也可以和c相连,这是三点其实只用枚举两次就可以,然而枚举c时就多枚举了。优化就显而易见的只枚举四个方向的最近点。

 1 #include<bits/stdc++.h>
 2 #define MAXN 6100
 3 #define reg register 
 4 #define INF 1000000000.0
 5 using namespace std;
 6 inline int read(){
 7     int s=0,w=0;char ch=getchar();
 8     while(ch<'0'||ch>'9')w=(ch=='-'),ch=getchar();
 9     while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
10     return w?-s:s;
11 }
12 #define kd (read())
13 int N,M,K;
14 int fat[MAXN];
15 struct dian{
16     int x,y;
17 }yd[MAXN];//圆点
18 int sf[MAXN][4];
19 inline int find(int x){
20     if(!fat[x]||fat[x]==x)return x;
21     return fat[x]=find(fat[x]);
22 }
23 void del(){for(reg int i=1;i<=K+2;++i)fat[i]=0; }//清并查集
24 double l,r;
25 double cal(int a,int b){
26     if(a==0||b==0)return INF;
27     return 1.0*sqrt(1.0*(yd[a].x-yd[b].x)*(yd[a].x-yd[b].x)+
28     1.0*(yd[a].y-yd[b].y)*(yd[a].y-yd[b].y)); 
29 }
30 bool _judge(double dist){
31     del();
32     /*for(reg int i=1;i<=K;++i)
33         for(reg int j=1;j<i;++j)
34             if(cal(yd[i],yd[j])<2.0*dist)
35                 fat[find(i)]=find(j);*/
36     for(reg int i=1;i<=K;++i)
37         for(reg int k=0;k<4;++k)
38             if(sf[i][k])
39                 if(find(i)!=find(sf[i][k]))
40                     if(cal(i,sf[i][k])<2.0*dist)
41                         fat[find(i)]=find(sf[i][k]);
42     for(reg int i=1;i<=K;++i){
43         if(1.0*M-yd[i].y<2.0*dist)
44             fat[find(K+1)]=find(i);
45         if(yd[i].y<2.0*dist)
46             fat[find(K+2)]=find(i);
47         if(find(K+2)==find(K+1))
48             return false;
49     }
50     if(find(K+2)==find(K+1))
51         return false;
52     return true;
53 }
54 int main(){
55     //freopen("da.in","r",stdin);
56     N=kd;M=kd;K=kd;
57     for(reg int i=1,a,b;i<=K;++i)
58         yd[i].x=kd,yd[i].y=kd;
59     for(reg int i=1;i<=K;++i){
60         for(reg int j=1;j<=K;++j)
61             if(i!=j){
62                 double dis=cal(i,j);
63                 if(yd[j].x<=yd[i].x&&yd[j].y<=yd[i].y)
64                     if(dis<cal(i,sf[i][0]))
65                         sf[i][0]=j;
66                 if(yd[j].x>=yd[i].x&&yd[j].y>=yd[i].y)
67                     if(dis<cal(i,sf[i][1]))
68                         sf[i][1]=j;
69                 if(yd[j].x<=yd[i].x&&yd[j].y>=yd[i].y)
70                     if(dis<cal(i,sf[i][2]))
71                         sf[i][2]=j;
72                 if(yd[j].x>=yd[i].x&&yd[j].y<=yd[i].y)
73                     if(dis<cal(i,sf[i][3]))
74                         sf[i][3]=j;
75             }
76         //for(int k=0;k<4;++k)
77         //    cout<<sf[i][k]<<" ";
78         //cout<<endl;
79     }
80     l=0;r=1000000.0;
81     while(r-l>(1e-8)){
82         double mid=(l+r)*0.5;
83         if(_judge(mid))l=mid;
84         else r=mid;
85     }
86     printf("%.7lf\n",r);
87 }
View Code

跑prim,O(n^2),贼快

其实思想差不多,每个点直接按距离连边,跑出来后找两个边界的路径的最大值除二,表示我找到一个权值最小的边界,在这个边界上最大只能是最大边除二。进一步解释,我过这条障碍线最大要为大边除二。在权值最小的防线上可以这样通过,那么在其他防线上(权值一定比此权值大)也一定能成立

posted @ 2019-08-17 21:49  bootpuss  阅读(215)  评论(0编辑  收藏  举报