[BZOJ2961]共点圆-[凸包+cdq分治]

 Description

传送门

Solution

考虑对于每一个点:

设圆的坐标为(x,y),点的坐标为(x0,y0)。依题意得,当一个点在圆里,需要满足(x-x0)2+(y-y0)2<=x2+y2

化简得x02+y02<=2x0*x+2y0*y。

当y0>0,x*(-x0/y0)+0.5y0+x02/(2*y0)<=y,这是一个半平面的式子;当y0<0时同理,但是要变号。

所以对于某个点(x0,y0),我们构造出在它前面所有圆心的凸包。凸包应分为上下。

通过以上式子我们可以得出,当y0>0时应在下凸包上找点(x,y)【该点为直线y=-x0/y0与下凸包的切点,即若此点满足要求,其他任何点都会满足要求。通过这个条件,我们也可以理解把该点理解为2x0*x+2y0*y最小的点】,反之则应该在上凸包上找。

好的让我们假设目前的y0>0,由于凸包上的点(x,y)是按极角排序,x*x0+y*y0是单峰的(这个式子是向量的点乘,其几何意义为:向量a点乘向量b=向量a的长度*向量b的长度*cos(向量a,b的夹角)。所以根据这个定义,证明,就画图吧。qaq正儿八经公式太麻烦了。)上凸包也是一样的。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const double eps=1e-11;
int n;
bool ans[500010];
struct W{
    int tp;double x,y;
    friend double operator *(W a,W b){return a.x*b.y-a.y*b.x;}
    friend double operator ^(W a,W b){return a.x*b.x+a.y*b.y;}
    friend W operator -(W a,W b){return W{0,a.x-b.x,a.y-b.y};}
    friend bool operator <(W a,W b){return (fabs(a.x-b.x)<eps)?a.y<b.y:a.x<b.x;}
}w[500010],t[500010],st[2][500010];//st[0]-上凸包,st[1]-下凸包 
double ask(W a,W b){return a.x*b.x+a.y*b.y;}
bool _ok[500010];
int top0,top1;
bool query(int id)
{
    int l,r,mid1,mid2;double minn=1e12;
    if (w[id].y<0)
    {
        l=1;r=top0;
        while (r-l>2)
        {
            mid1=(l*2+r)/3;mid2=(r*2+l)/3;
            if ((w[id]^st[0][mid1])<(w[id]^st[0][mid2])) r=mid2;
            else l=mid1;
        }
        for (int i=l;i<=r;i++) minn=min(minn,w[id]^st[0][i]);
        
    } else
    {
        l=1;r=top1;
        while (r-l>2)
        {
            mid1=(l*2+r)/3;mid2=(r*2+l)/3;
            if ((w[id]^st[1][mid1])<(w[id]^st[1][mid2])) r=mid2;
            else l=mid1;
        }
        for (int i=l;i<=r;i++) minn=min(minn,w[id]^st[1][i]);
    }
    if (2*minn-(w[id].x*w[id].x+w[id].y*w[id].y)<eps) ans[id]=0;
    
}
void solve(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2,tot=0;
    solve(l,mid);
    solve(mid+1,r);
    for (int i=l;i<=mid;i++) if (!w[i].tp) t[++tot]=w[i];
    sort(t+1,t+tot+1);
    top1=0,top0=0;
    for (int i=1;i<=tot;i++)
    {
        while (top0>1&&(st[0][top0]-st[0][top0-1])*(t[i]-st[0][top0])>-eps) top0--;
        st[0][++top0]=t[i];
    }
    st[0][top0+1].x=st[0][top0].x;st[0][top0+1].y=-1e12;
    for (int i=tot;i;i--)
    {
        while (top1>1&&(st[1][top1]-st[1][top1-1])*(t[i]-st[1][top1])>-eps) top1--;
        st[1][++top1]=t[i];
    }
    st[1][top1+1].x=st[1][top1].x;st[1][top1+1].y=1e12;
    for (int i=mid+1;i<=r;i++) if (w[i].tp&&ans[i]) query(i);
}
bool _is=0;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%lf%lf",&w[i].tp,&w[i].x,&w[i].y);
        if (!w[i].tp) _is=1;else ans[i]=_is;
    }
    solve(1,n);
    for (int i=1;i<=n;i++) if (w[i].tp)
     if (ans[i]) printf("Yes\n");else printf("No\n");
}

 

posted @ 2018-08-20 22:04  _雨后阳光  阅读(284)  评论(0编辑  收藏  举报