【洛谷】P1783 海滩防御 (二分,并查集)
-
题意:两条垂直边之间有\(n\)个点,问点的半径至少为多少可以将两条边完全封死。
-
题解:首先对半径进行二分,然后分别统计能到达左边和右边的点,将相交的点用并查集合并起来,然后判断能到达左边的点和右边的点是否在同一个集合即可。
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N= 1e6+10; const int mod=1e9+7; const int INF= 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,m; pair<double,double> pt[N]; int p[N]; int L[N],R[N]; double get_dis(double x1,double y1,double x2,double y2){ return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } int find(int x){ if(p[x]!=x) p[x]=find(p[x]); return p[x]; } int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=m;++i){ scanf("%lf %lf",&pt[i].fi,&pt[i].se); } double l=0.0,r=1e9; while(r-l>1e-8){ double mid=(l+r)/2.0; for(int i=1;i<=n;++i) p[i]=i,L[i]=0,R[i]=0; int cnt1=0,cnt2=0; bool flag=false; for(int i=1;i<=n;++i){ if(pt[i].fi-mid<=0.0) L[++cnt1]=i; if(pt[i].fi+mid>=(double)n) R[++cnt2]=i; for(int j=i+1;j<=n;++j){ double dis=get_dis(pt[i].fi,pt[i].se,pt[j].fi,pt[j].se); if(dis<=2*mid){ int fa=find(i); int fb=find(j); p[fa]=fb; } } for(int i=1;i<=cnt1;++i){ for(int j=1;j<=cnt2;++j){ int fa=find(L[i]); int fb=find(R[j]); if(fa==fb){ flag=true; break; } } if(flag) break; } if(flag) break; } if(flag) r=mid; else l=mid; } printf("%.2f\n",r); return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮