[CDQ分治 凸包] BZOJ 2961 共点圆

%%% PoPoQQQ :http://blog.csdn.net/PoPoQQQ/article/details/42318247


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

直接用数据结构维护这些点和圆不是很好写,我们考虑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的情况要特判

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


详见2013 xhr 论文

PS 调戏了半天 Quickhull打错了


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

inline int dcmp(double a,double b){
	if (fabs(a-b)<1e-15) return 0;
	if (a<b) return -1;
	return 1;
}

struct Point{
	double x,y;
	Point(double x=0,double y=0):x(x),y(y) { }
	bool operator < (const Point &B) const{
		return dcmp(x,B.x)==0?y<B.y:x<B.x;
	}
	void read(){
		scanf("%lf%lf",&x,&y);
	}
	friend double Cross(Point p1,Point p2,Point p0){
		return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
	}
	friend double calc(Point p1,Point p2){
		if (dcmp(p1.x,p2.x)==0) return 1e100;
		return (p2.y-p1.y)/(p2.x-p1.x);
	}
	friend double Distance(const Point p1,const Point p2=Point(0,0))  
	{  
    	return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) );  
	}  
}P[500005],hull[500005];
double s[500005];
int num,cnt;

struct event{
	int f,idx;
	Point p;
}eve[500005];

int n,icnt;
int ans[500005];

inline void Qhull(int l,int r,Point a,Point b)
{
	int i=l-1,j=r+1,x=l;
	for (int k=l;k<=r;k++)
		if (dcmp(s[k],s[x])>0 || (dcmp(s[k],s[x])==0 && P[x]<P[k]))
			x=k;
	Point y=P[x];
	for (int k=l;k<=r;k++)
	{
		s[++i]=Cross(P[k],a,y);
		if (dcmp(s[i],0)>0) swap(P[k],P[i]); else s[i--]=0;
	}
	for (int k=r;k>=l;k--)
	{
		s[--j]=Cross(P[k],y,b);
		if (dcmp(s[j],0)>0) swap(P[j],P[k]); else s[j++]=0;
	}
	if (l<=i) Qhull(l,i,a,y);
	hull[++cnt]=y;
	if (j<=r) Qhull(j,r,y,b);
}

inline void QHULL(int l,int r){
	num=cnt=0;
	int x=1;
	for (int i=l;i<=r;i++)
		if (eve[i].f==0)
		{
			P[++num]=eve[i].p;
			if (P[num]<P[x]) x=num;
			s[num]=0;
		}
	if (num==0) return;
	swap(P[1],P[x]);
	hull[++cnt]=P[1];
	if (num>=2) Qhull(2,num,P[1],P[1]);
}

double sk[500005],xk[500005];
int scnt,xcnt;
int lp,rp;

inline void cdq(int l,int r){
	if (l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid); cdq(mid+1,r);
	QHULL(l,mid);
	if (cnt==0) return;
	lp=1,rp=1;
	for (int i=1;i<=cnt;i++)
	{
		if (hull[i].x<hull[lp].x) lp=i;
		if (hull[i].x>hull[rp].x) rp=i;
	}
	sk[scnt=1]=-1e130;
	for (int i=rp;i!=lp;i=i==1?cnt:i-1)
	{
		sk[++scnt]=calc(hull[i],hull[i==1?cnt:i-1]);
		if (sk[scnt]==1e100 && scnt==2) sk[scnt]=-1e100;
	}
	sk[++scnt]=1e130;
	xk[xcnt=1]=-1e130;
	for (int i=lp;i!=rp;i=i==1?cnt:i-1)
	{
		xk[++xcnt]=calc(hull[i],hull[i==1?cnt:i-1]);
		if (xk[xcnt]==1e100 && xcnt==2) xk[xcnt]=-1e100;
	}
	xk[++xcnt]=1e130;
	int iter,pos; double K; Point p;
	for (int i=mid+1;i<=r;i++)
		if (eve[i].f==1)
		{
			if (eve[i].idx==367)
				int c=1;
			p=eve[i].p; K=-p.x/p.y;
			if (dcmp(p.y,0)>0)
			{
				iter=lower_bound(xk+1,xk+xcnt+1,K)-xk;
				pos=(((lp-(iter-1)+1)%cnt)+cnt)%cnt;
				if (pos==0) pos=cnt;
				if (Distance(hull[pos])<Distance(hull[pos],p))
					ans[eve[i].idx]=0;
			}
			else if (dcmp(p.y,0)<0)
			{
				iter=lower_bound(sk+1,sk+scnt+1,K)-sk;
				pos=(((rp-(iter-1)+1)%cnt)+cnt)%cnt;
				if (pos==0) pos=cnt;
				if (Distance(hull[pos])<Distance(hull[pos],p))
					ans[eve[i].idx]=0;
			}
			else if (dcmp(p.y,0)==0)
			{
				if (p.x>0)
					pos=lp;
				else
					pos=rp;
				if (Distance(hull[pos])<Distance(hull[pos],p))
					ans[eve[i].idx]=0;
			}
		}
}

int main()
{
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	scanf("%d",&n);
	int flag=0;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&eve[i].f),eve[i].p.read();
		if (eve[i].f==1)
			eve[i].idx=++icnt,ans[icnt]=flag;
		else
			flag=1;
	}
	cdq(1,n);
	for (int i=1;i<=icnt;i++)
		if (ans[i])
			printf("Yes\n");
		else
			printf("No\n");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值