1. 程式人生 > >【CF744D】Hongcow Draws a Circle 二分+幾何

【CF744D】Hongcow Draws a Circle 二分+幾何

復雜 pri 極角 開始 存在 pan while poi 二分答案

【CF744D】Hongcow Draws a Circle

題意:給你平面上n個紅點和m個藍點,求一個最大的圓,滿足圓內不存在藍點,且至少包含一個紅點。

$n,m\le 10^3$

題解:我們先不考慮半徑為inf的情況。顯然所求的圓一定是要與某個藍點相切的。我們可以先枚舉這個藍點,然後二分答案。當半徑已知、一個點固定時,圓的可能位置只能是繞著一個點旋轉得到的結果,其余的所有點都對應著極角上的一段區間,我們可以將這些區間排序,采用掃描線,看一下是否存在一段區間包含紅點且不包含藍點即可。

但是如果你仔細分析的話你會發現這樣的二分是不滿足單調性的。不過如果我們一開始不光枚舉藍點,還枚舉所有紅點,一起進行二分,這樣就滿足單調性了。

直接做的復雜度是$O(n\log ^2 n)$,會TLE,看了標程加了一些神優化才過~具體見代碼。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define pi acos(-1.0)
using namespace std;
typedef long double db;
const db eps=1e-12;
const int maxn=1010;
struct point
{
	db x,y;
	point() {}
	point(db a,db b) {x=a,y=b;}
	point operator + (const point &a) const {return point(x+a.x,y+a.y);}
	point operator - (const point &a) const {return point(x-a.x,y-a.y);}
	db operator * (const point &a) const {return x*a.y-y*a.x;}
	point operator * (const db &a) const {return point(x*a,y*a);}
}p[maxn<<1];
struct line
{
	point p,v;
	line() {}
	line(point a,point b) {p=a,v=b;}
};
struct node
{
	db x;
	int k;
	node() {}
	node(double a,int b) {x=a,k=b;}
}q[maxn<<3];
int n,m,tot;
inline db dis(point a,point b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
db getrange(point a,point b,db R)
{
	db d=dis(a,b)/2;
	return acos(d/R);
}
bool cmp(const node &a,const node &b) {return a.x<b.x;}
inline bool solve(int x,db R)
{
	int i;
	tot=0;
	if(x<=n)	q[++tot]=node(-pi,1),q[++tot]=node(pi,-1);
	else
	{
		for(i=1;i<=n;i++)
		{
			if(dis(p[i],p[x])>R+R-eps)	continue;
			db a=getrange(p[x],p[i],R),b=atan2(p[i].y-p[x].y,p[i].x-p[x].x);
			db c=b-a,d=b+a;
			if(c<-pi)	c+=2*pi;
			if(d>pi)	d-=2*pi;
			if(c<d)	q[++tot]=node(c,1),q[++tot]=node(d,-1);
			else	q[++tot]=node(-pi,1),q[++tot]=node(d,-1),q[++tot]=node(c,1),q[++tot]=node(pi,-1);
		}
	}
	for(i=n+1;i<=n+m;i++)
	{
		if(dis(p[i],p[x])>R+R-eps)	continue;
		db a=getrange(p[x],p[i],R),b=atan2(p[i].y-p[x].y,p[i].x-p[x].x);
		db c=b-a,d=b+a;
		if(c<-pi)	c+=2*pi;
		if(d>pi)	d-=2*pi;
		if(c<d)	q[++tot]=node(c,-10000),q[++tot]=node(d,10000);
		else	q[++tot]=node(-pi,-10000),q[++tot]=node(d,10000),q[++tot]=node(c,-10000),q[++tot]=node(pi,10000);
	}
	sort(q+1,q+tot+1,cmp);
	int tmp=0;
	for(i=1;i<=tot;i++)
	{
		if(tmp>0&&i!=1&&q[i].x>q[i-1].x+eps)	return 1;
		tmp+=q[i].k;
	}
	return 0;
}
inline bool check(db mid)
{
	for(int i=1;i<=n+m;i++)	if(solve(i,mid))	return 1;
	return 0;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	if(m==1)
	{
		puts("-1");
		return 0;
	}
	int i;
	for(i=1;i<=n;i++)	p[i].x=rd(),p[i].y=rd();
	random_shuffle(p+1,p+n+1);
	for(i=1;i<=m;i++)	p[i+n].x=rd(),p[i+n].y=rd();
	random_shuffle(p+n+1,p+m+1);
	db l=0,r,mid;
	for(i=1;i<=n+m;i++)	if(solve(i,l))	//神優化
	{
		r=1e9;
		while(r-l>1e-5)
		{
			mid=(l+r)/2;
			if(solve(i,mid))	l=mid;
			else	r=mid;
		}
	}
	if(l>1e9-1)	puts("-1");
	else	printf("%.18Lf",l);
	return 0;
}

【CF744D】Hongcow Draws a Circle 二分+幾何