6489. 【GDOI2020模拟02.29】守卫疆域guard
题目描述
题解
感觉枚举两个点之后二分半径不满足二分性,因此枚举了两个点之后对于剩下的点找圆心在这两个点的中垂线上的区间
然后常数就炸了(
二分答案,枚举一个在边界上的点,考虑把圆绕着该点转一圈
那么对于其他的点就可以表示成一个区间,在区间内会被计算到
排序判断即可,注意判断完全碰不到的情况
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define abs(x) ((x)>0?(x):-(x))
#define sqr(x) ((x)*(x))
#define inf 2133333333
#define E 0.000000001
#define ll long long
#define file
using namespace std;
struct type{
double x,y;
} a[501];
struct Type{
double x;
int s;
} b[2001];
int n,m,i,j,k,l,tot;
double L,R,Mid,ans,dis;
bool cmp(Type a,Type b) {return abs(a.x-b.x)>E && a.x<b.x || abs(a.x-b.x)<=E && a.s>b.s;}
void add(double t1,double t2)
{
b[++tot]={t1,1};
b[++tot]={t2,-1};
}
bool pd(double R)
{
int i,j,k,l,sum;
double dis,theta,theta2;
fo(i,1,n)
{
sum=tot=0;
fo(j,1,n)
{
dis=sqrt(sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y));
if (dis<=E)
++sum;
else
if (R*2>=dis)
{
theta=atan2(a[j].y-a[i].y,a[j].x-a[i].x);
theta2=acos(dis/(R*2));
if (theta<0) theta+=M_PI*2;
if (theta-theta2<0)
add(theta-theta2+M_PI*2,M_PI*2),add(0,theta+theta2);
else
if (theta+theta2>M_PI*2)
add(theta-theta2,M_PI*2),add(0,theta+theta2-M_PI*2);
else
add(theta-theta2,theta+theta2);
}
}
if (!tot)
{
if (sum>=m)
return 1;
continue;
}
if (sum+tot/2<m) continue;
stable_sort(b+1,b+tot+1,cmp);
fo(j,1,tot)
{
sum+=b[j].s;
if (sum>=m) return 1;
}
}
return 0;
}
int main()
{
freopen("guard.in","r",stdin);
#ifdef file
freopen("guard.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
fo(i,1,n)
scanf("%lf%lf",&a[i].x,&a[i].y);
if (m==1)
{
printf("0\n");
return 0;
}
else
if (m==2)
{
ans=inf;
fo(i,1,n-1)
{
fo(j,i+1,n)
{
dis=sqrt(sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y));
ans=min(ans,dis/2);
}
}
printf("%.10lf\n",ans);
return 0;
}
L=0;
R=7072;
while (R-L>0.0001)
{
Mid=(L+R)/2;
if (!pd(Mid))
L=Mid;
else
R=Mid;
}
printf("%.10lf\n",L);
fclose(stdin);
fclose(stdout);
return 0;
}