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