BZOJ2961: 共点圆【二进制分组|CDQ+凸包+三分】

本文详细解析了共点圆的数学问题,通过将圆方程转换为不等式,运用二进制分组和凸包算法进行求解,介绍了CDQ分治策略,并提供了具体的C++实现代码,展示了如何处理大量圆的共点判断。

2961: 共点圆

根据圆方程 ( x − x 0 ) 2 + ( y − y 0 ) 2 ≤ ( x 0 2 + y 0 2 ) 2 (x-x_0)^2+(y-y_0)^2 \le (x_0^2+y_0^2)^2 (xx0)2+(yy0)2(x02+y02)2

解得 x 0 2 + y 0 2 ≤ 2 x x 0 + 2 y y 0 x_0^2+y_0^2\le 2xx_0+2yy_0 x02+y022xx0+2yy0

右边项可以看成 ( 2 x , x 0 ) ⋅ ( 2 y , y 0 ) (2x,x_0)·(2y,y_0) (2x,x0)(2y,y0)

所以我们二进制分组+凸包就可以了。

CDQ分治也可以。

#include<stack>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=5e5+5;
const double EXP=1e-8;
int n,q,Top,LstAns,S[MAXN],Len[MAXN],tot;
struct Point{
	double x,y;
	bool operator <(const Point b)const{return x==b.x?y<b.y:x<b.x;}
	double operator ^(const Point b)const{return x*b.y-y*b.x;}
	double operator *(const Point b)const{return x*b.x+y*b.y;}
	Point operator -(const Point b)const{return (Point){x-b.x,y-b.y};}
}Stk[MAXN],a[MAXN];
vector<Point> TB[32];
void Make_TB(int p,int L,int R){
	TB[p].clear();Top=0;sort(a+L,a+R+1);
	for(int i=L;i<=R;i++){
		while(Top>1&&((a[i]-Stk[Top])^(Stk[Top]-Stk[Top-1]))>=0) Top--;
		Stk[++Top]=a[i];
	}
	for(int i=1;i<=Top;i++) TB[p].push_back(Stk[i]);
}
void Insert(Point x){
	a[++n]=x;for(int mi=1;mi==Len[tot]&&tot;mi*=2) tot--;++tot;
	S[tot]=S[tot-1]+Len[tot-1];Len[tot]=n-S[tot];
	Make_TB(tot,S[tot]+1,S[tot]+Len[tot]);
}
double Find(int p,int L,int R,Point x){
	double ret=1e9;
	while(L<=R){
		if(R-L<=3) break;
		int mid1=L+(R-L)/3,mid2=R-(R-L)/3;
		if(TB[p][mid1]*x>TB[p][mid2]*x) L=mid1;else R=mid2;
	}
	for(int i=L;i<=R;i++) ret=min(ret,TB[p][i]*x);
	return ret;
}
double _abs(double x){return x<0?-x:x;}
//bool cmp(double x){if(_abs(x)<=EXP||x<=0) return 1;else return 0;}
void Query(Point x){
	double Ans=1e9;
	for(int i=1;i<=tot;i++) Ans=min(Ans,Find(i,0,TB[i].size()-1,x));
	if(Ans>=x.x*x.x+x.y*x.y&&tot>0) LstAns++,puts("Yes");else puts("No");
}
int main(){
//	freopen("4140.in","r",stdin);
//	freopen("4140.out","w",stdout);
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int Type;double x,y;scanf("%d%lf%lf",&Type,&x,&y);x+=LstAns,y+=LstAns;
		if(Type) Query((Point){x,y});else Insert((Point){x*2,y*2});
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值