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;
}
时间: 2024-10-12 04:02:50

BZOJ 2961 共点圆 CDQ分治+凸包的相关文章

BZOJ 3963: [WF2011]MachineWorks [CDQ分治 斜率优化DP]

传送门 当然了WF的题uva hdu上也有 你的公司获得了一个厂房N天的使用权和一笔启动资金,你打算在这N天里租借机器进行生产来获得收益.可以租借的机器有M台.每台机器有四个参数D,P,R,G.你可以在第D天花费P的费用(当然,前提是你有至少P元)租借这台机器,从第D+1天起,操作机器将为你产生每天G的收益.在你不再需要机器时,可以将机器卖掉,一次性获得R的收益.厂房里只能停留一台机器.不能在购买和卖出机器的那天操作机器,但是可以在同一天卖掉一台机器再买入一台.在第N+1天,你必须卖掉手上的机器

bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组

Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小 接下来每行为一下三种输入之一(不包含引号): "1 x y a" "2 x1 y1 x2 y2" "3" 输入1:你需要把(x,y)(第x行第y列)的格子权值增加a 输入

BZOJ 1176 Mokia(cdq分治,解决一类在线查询问题)

题意:维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. 这题在bz是贵族题= =,没有链接了 解法:cdq分治,第一维是时间t,第二维是x,第三维是y:每个操作看作一个三维序(t, x, y):假设修改操作是(t, x, y),两个查询操作分别是(t1, x1, y1) 和 (t1, x2, y2):实际上就要问,有多少个修改操作,满足t<=t1,x1<=x&

BZOJ 1176 [Balkan2007]Mokia CDQ分治

题目大意: 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. POJ1195的加强版 没记错的话上午这题还没有中文题目描述的说0.0 好迅速 首先这题看到W就知道二维树状数组挂了 看到M就发现离散化了也搞不了 0.0 这题似乎是CDQ分治被发现之后第二个解决的题目...不过只有会员才知道的世界,今天反应过来刷刷... 修改和询问放在一起分治,一个询问拆分成4个,树状数组处

bzoj 1176 Mokia(CDQ分治)

[题目链接]  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=96974 [题意] 定义查询操作与修改操作:1 x y z 为将格子(x,y)修改为z:2 x1 y1 x2 y2为查询以(x1,y1)为左上(x2,y2)为右下的子矩阵之和. [思路] cdq分治. 矩阵初始值都为s,所以先不考虑. 首先明确每一个修改操作都互不影响.然后把每一个对子矩阵的询问操作差分为对四个点的“前缀和”操作. 先把所有的操作按照x升序排列

BZOJ 2683 简单题 cdq分治+树状数组

题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用. 首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响. 我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这

bzoj 2683 简单题 cdq分治

题面 题目传送门 解法 可以离线,那么就是非常简单的cdq分治了 只要把询问拆成4个,然后就变成了一个三维偏序问题 时间复杂度:\(O(q\ log^2\ n)\) 代码 #include <bits/stdc++.h> #define int long long #define N 1000010 using namespace std; template <typename node> void chkmax(node &x, node y) {x = max(x, y

BZOJ 2683 简单题 ——CDQ分治

简单题 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define maxn 2000005 int sum[maxn]; void a

BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或者留言我. 首先这道题能推出个表达式 f[i]代表第i天最大收益. xx[i]表示将第i天的钱都买A的数量 yy[i]表示将第i天的钱都买B的数量 所以f[i]=max(f[i?1],p[i].a?xx[j]+p[i].b?yy[j])j<i 所以我们要维护这个n^2的递推式 又知道f[i]是由小于