【BZOJ】2961: 共点圆 -圆的反演&cdq分治/二进制分组

传送门:bzoj2961

我很绝望,bzoj WA \text{WA} WA了两页了还是没过,调了两天!

单独把询问点 y = 0 y=0 y=0的情况提出来算了发现网上好多题解的代码都判 WA \text{WA} WA了,然而除了询问点 y = 0 y=0 y=0的情况都没对拍出错来TAT。

我真傻,真的,斜率 k k k设成 int \text{int} int都没发现。
&判上/下凸壳一定要用斜率比,而不是叉积…


题解

假设查询点坐标为 ( x , y ) (x,y) (x,y),每个圆心坐标分别为 ( x i , y i ) (x_i,y_i) (xi,yi),当前点在所有圆内需满足:
x i 2 + y i 2 ≥ ( x i − x ) 2 + ( y i − y ) 2 x_i^2+y_i^2\geq (x_i-x)^2+(y_i-y)^2 xi2+yi2(xix)2+(yiy)2
转化一下:
2 y y i ≥ − 2 x x i + x 2 + y 2 2yy_i\geq -2xx_i+x^2+y^2 2yyi2xxi+x2+y2

y > 0 y>0 y>0时:
y i ≥ − x y x i + x 2 + y 2 2 y y_i\geq -\dfrac{x}{y}x_i+\dfrac{x^2+y^2}{2y} yiyxxi+2yx2+y2

也就是所有点都在 y = − x y x + x 2 + y 2 2 y y=-\dfrac{x}{y}x+\dfrac{x^2+y^2}{2y} y=yxx+2yx2+y2之上。

y &lt; 0 y&lt;0 y<0时:
y i ≤ − x y x i + x 2 + y 2 2 y y_i\leq -\dfrac{x}{y}x_i+\dfrac{x^2+y^2}{2y} yiyxxi+2yx2+y2
也就是所有点都在 y = − x y x + x 2 + y 2 2 y y=-\dfrac{x}{y}x+\dfrac{x^2+y^2}{2y} y=yxx+2yx2+y2之下。

y = 0 y=0 y=0的情况单独提出来直接维护即可。

于是可以对所有圆心坐标分别维护上凸壳和下凸壳, y &gt; 0 y&gt;0 y>0 y &lt; 0 y&lt;0 y<0时分别在下凸壳和上凸壳上找到对应的切点计算即可。

于是可以 c d q cdq cdq分治,使得点坐标和询问直线斜率有序化,构造左边部分的上下凸壳,拿右边询问扫一遍即可。

在线的(bzoj4140共点圆加强版)可以二进制分组维护。


代码

WA

#include<bits/stdc++.h>
#define prit(x) cerr<<#x<<":"<<x<<endl
using namespace std;
const int N=5e5+10;
typedef long double db;
const db eps=1e-8;

int n,m,stk[N],top;
bool pr[N],ans[N],exi;db l,r;

struct P{
	db x,y;
	P(db x_=0.0,db y_=0.0):x(x_),y(y_){};
    P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
    P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
    db operator ^(const P&ky){return x*ky.y-y*ky.x;}
    P operator *(const db&ky){return P(x*ky,y*ky);}
    P operator /(const db&ky){return P(x/ky,y/ky);}
}a[N],b[N],ori;

int dcmp(db x){if(fabs(x)<eps) return 0;return x>0?1:-1;}

struct Q{
	int typ,id,dr;db k;P p;
	bool operator <(const Q&ky)const{
	   if(typ^ky.typ) return typ<ky.typ;
	   return typ?k<ky.k:p.x<ky.p.x;
	}
}q[N],p[N];

db slope(P a,P b)
{
	if(!dcmp(a.x-b.x)) return a.y<b.y?1e18:-1e18;
	return (a.y-b.y)/(a.x-b.x);
}

db dis(P a,P b){return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);}

bool uvalid(P a,P b)
{
	return dis(b,ori)<dis(a,b);
//return 2*a.p.x*b.x+2*a.p.y*b.y<sqr(a.p.x)+sqr(a.p.y);
}

void sol(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1,i=l,j=mid+1,k,pos,ta,tb,qa,qb;
	for(k=l;k<=r;++k) 
	  if(q[k].dr>mid) p[j++]=q[k];
	  else p[i++]=q[k];
	for(k=l;k<=r;++k) q[k]=p[k]; 
	for(pos=mid+1;pos<=r && (!q[pos].typ);++pos);
    if((!q[l].typ)&&pos<=r){
    	ta=tb=0;
    	for(i=l;i<=mid && (!q[i].typ);++i){
    		for(;ta>1 && slope(a[ta-1],q[i].p)-eps<slope(a[ta-1],a[ta]);--ta);a[++ta]=q[i].p;
    		for(;tb>1 && slope(b[tb-1],q[i].p)+eps>slope(b[tb-1],b[tb]);--tb);b[++tb]=q[i].p;
    	}
        qa=1;qb=tb;
        for(i=pos;i<=r;++i)
         if(q[i].p.y<0){
         	 for(;qb>1 && slope(b[qb-1],b[qb])<q[i].k;--qb);
         	 if(qb>0 && ans[q[i].id] && uvalid(q[i].p,b[qb])) ans[q[i].id]=false;
         }else{
         	 for(;qa<ta && slope(a[qa],a[qa+1])<q[i].k;++qa);
         	 if(qa<=ta && ans[q[i].id] && uvalid(q[i].p,a[qa])) ans[q[i].id]=false;
         }
	}
    sol(l,mid);sol(mid+1,r);
}

int main(){
//	freopen("ty.txt","r",stdin);
	int i;double x,y;db ql,qr;
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%d%lf%lf",&q[++m].typ,&x,&y);
		q[m].p=P(x,y);
		if(q[m].typ){
			pr[i]=ans[i]=true;
			if(!exi) {ans[i]=false;m--;continue;}
			if(fabs(y)<eps){
			  if(x<l || x>r) 
			    ans[i]=false;
			  m--;continue;
			}
			q[m].k=-x/y;
 //           q[m].k=(fabs(y)<eps)?1e18:(-x/y);
		}else{
			ql=0,qr=x+x;if(ql>qr) swap(ql,qr);
			if(!exi) l=ql,r=qr;else l=max(l,ql),r=min(r,qr);
			exi=true;
		}
		q[m].id=i;q[m].dr=m;
	}
	sort(q+1,q+m+1);sol(1,m);
    for(i=1;i<=n;++i) if(pr[i]) puts(ans[i]?"Yes":"No");
    return 0;
}
/*
9
0 2 3
0 4 1
1 1 1
0 -3 2
1 0 0
1 2 0
1 3 0
1 0 1
1 1 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值