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