首页 > 代码库 > BZOJ 2961 共点圆 CDQ分治+凸包

BZOJ 2961 共点圆 CDQ分治+凸包

题目大意:给定平面,多次插入点和圆,每次插入点时询问当前插入的点是否在之前插入的所有圆中并且至少在一个圆中

直接用数据结构维护这些点和圆不是很好写,我们考虑CDQ分治

对于每层分治,我们需要对于[mid+1,r]中的每个点求出[l,mid]中是否所有的圆都覆盖了这个点

设点的坐标为(x0,y0),那么这个点在所有圆内必须满足对于所有的圆心(x,y),(x-x0)^2+(y-y0)^2<=x^2+y^2,即2*x*x0+2*y*y0>=x0^2+y0^2

我们发现上面的式子是一个半平面,斜率为-x0/y0,如果所有的圆心都在这个半平面中,那么这个点就在所有的圆内。

我们可以对所有的圆心做一个凸包(上下凸包都要做),保证[mid+1,r]部分斜率递增,然后对于每个点对应的半平面,去凸包上查找卡到的点

如果这个点在卡到的圆之内 那么它就在[l,mid]中所有圆之内 反之则不在

然后就正常做好了- - 如果一个点的y0为正 就在下凸包上查询 反之则在上凸包上查询

由于斜率递增 所以下凸包应该维护一个队列,而上凸包则需要维护一个栈 y0=0的情况要特判

此外注意左侧没有圆的情况- -

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
#define EPS 1e-5
#define INF 1e11
using namespace std;
struct Point{
	double x,y;
	Point() {}
	Point(double _,double __):x(_),y(__) {}
};
struct Query:Point{
	int type;
	double k;
	Query() {}
	Query(int _,double __,double ___):type(_)
	{
		x=__;y=___;
		if(fabs(y)<=EPS)
			k=INF;
		else k=-x/y;
	}
	bool operator < (const Query &q) const
	{
		return x < q.x;
	}
}mempool[M],*q[M],*nq[M];
int n;
bool outside_the_circles[M],appeared;
bool Compare(Query* q1,Query* q2)
{
	return q1->k < q2->k;
}
inline double Get_Slope(const Point &p1,const Point &p2)
{
	if(fabs(p1.x-p2.x)<EPS)
		return p1.y<p2.y?INF:-INF;
	return (p1.y-p2.y)/(p1.x-p2.x);
}
inline double Distance(const Point &p1,const Point &p2)
{
	return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) );
}
void CDQ_Divide_And_Conquer(int x,int y)
{
	static Point *queue[M],*stack[M];
	//queue-下凸包 stack-上凸包
	int i,mid=x+y>>1;
	if(x==y) return ;
	int l1=x,l2=mid+1;
	for(i=x;i<=y;i++)
	{
		if(q[i]-mempool<=mid)
			nq[l1++]=q[i];
		else
			nq[l2++]=q[i];
	}
	memcpy(q+x,nq+x,sizeof(q[0])*(y-x+1) );
	CDQ_Divide_And_Conquer(x,mid);
	int top=0,r=0,h=0;
	for(i=x;i<=mid;i++)
	{
		if(q[i]->type==1) continue;
		Point p=*q[i];
		while( top>=2 && Get_Slope(*stack[top-1],*stack[top])<Get_Slope(*stack[top],p) )
			stack[top--]=0x0;
		stack[++top]=q[i];
		while( r-h>=2 && Get_Slope(*queue[r-1],*queue[r])>Get_Slope(*queue[r],p) )
			queue[r--]=0x0;
		queue[++r]=q[i];
	}
	for(i=mid+1;i<=y;i++)
	{
		if(q[i]->type==0) continue;
		if(q[i]->y>EPS)
		{
			while( r-h>=2 && Get_Slope(*queue[h+1],*queue[h+2])<q[i]->k )
				queue[++h]=0x0;
			if( r-h>=1 && Distance(*queue[h+1],Point(0,0) ) < Distance(*queue[h+1],*q[i]) )
				outside_the_circles[q[i]-mempool]=1;
		}
		else
		{
			while( top>=2 && Get_Slope(*stack[top-1],*stack[top])<q[i]->k )
				stack[top--]=0x0;
			if( top>=1 && Distance(*queue[h+1],Point(0,0) ) < Distance(*queue[h+1],*q[i]) )
				outside_the_circles[q[i]-mempool]=1;
		}
	}
	CDQ_Divide_And_Conquer(mid+1,y);
	l1=x;l2=mid+1;
	for(i=x;i<=y;i++)
	{
		if( l2 > y || l1<=mid && *q[l1]<*q[l2] )
			nq[i]=q[l1++];
		else
			nq[i]=q[l2++];
	}
	memcpy(q+x,nq+x,sizeof(q[0])*(y-x+1) );
}
int main()
{
	int i,type;
	double x,y;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		scanf("%d%lf%lf",&type,&x,&y);
		q[i]=&( mempool[i]=Query(type,x,y) );
	}
	sort(q+1,q+n+1,Compare);
	CDQ_Divide_And_Conquer(1,n);
	for(i=1;i<=n;i++)
	{
		if(mempool[i].type==0)
			appeared=1;
		else
			puts(appeared&&!outside_the_circles[i]?"Yes":"No");
	}
	return 0;
}


BZOJ 2961 共点圆 CDQ分治+凸包