bzoj2961 共点圆(cdq分治维护凸包,计算几何)

这题真是写的我有点绝望。菜不禁声。其实一开始是想拿KD tree树写的,听说能过,但是wa成狗??至今改不对gg。一个点(x0,y0)在圆(x,y)内,要求
(xx0)2+(yy0)2<=x2+y2
化简一下就是
x20+y20<=2x0x+2y0y
假设 y0>0 ,我们有 y>=x0y0x+x20+y202y0 ,是一个半平面!
也就是说我们现在把问题转化为:对于每一个询问,相当于给定了一个半平面,问你是否所有点都在这个半平面上。
这个东西我们可以维护一个凸包(因为y0正负未定,所以要维护上下两个凸包),每次拿着半平面的斜率去在凸包上二分,找到那个卡到的点,只需判定这个点在不在半平面上即可。如果y0>0,去下凸包上找,否则去上凸包上找。至于为什么…我只能形象的理解一下,就是y0>0时,我们的半平面是在一条直线的上方,我们找到下凸包对于这条直线而言最下面的那个点,只要他都在直线上方,那么剩下的点也一定都在直线上方。然后我们可以cdq分治,利用特殊的技巧来维护。(先都按斜率从小到大排序,然后每次分治时按标号分两部分,先递归处理[l,mid],然后为了方便线性处理凸包,我们每次分治完按x坐标排好序。我们处理好[l,mid]后,做出[l,mid]中的凸包,然后因为此时[mid+1,r]还是按斜率从小到大排好序的,我们可以线性的扫一遍更新答案。然后再去递归处理[mid+1,r])。复杂度 O(nlogn)
还有一些理解可以见2013集训队论文xhr神犇的。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define inf 1e40
#define N 500010
#define eps 1e-10
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,tot=0,q1[N],q2[N];//q1,下凸包,q2,上凸包
bool ans[N],one=0;
struct point{
    double x,y;
};
struct event{
    point x;int op,id,qid;double k; 
}e[N],tmp[N];
inline bool cmp(event a,event b){return a.k<b.k;}
inline double abs(double x){return x<0?-x:x;}
inline double sqr(double x){return x*x;}
inline double slope(point a,point b){
    if(abs(b.x-a.x)<eps) return inf;
    return (b.y-a.y)/(b.x-a.x);
}
inline double dis(point a,point b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
inline double R(point a){return sqr(a.x)+sqr(a.y);}
inline void cdq(int l,int r){
    if(l==r) return;
    int mid=l+r>>1,p1=l,p2=mid+1,h1=1,h2=1,t1=0,t2=0;
    for(int i=l;i<=r;++i)
        if(e[i].id<=mid) tmp[p1++]=e[i];
        else tmp[p2++]=e[i];
    memcpy(e+l,tmp+l,sizeof(e[0])*(r-l+1));
    cdq(l,mid);
    for(int i=l;i<=mid;++i){
        if(e[i].op) continue;
        while(h1<t1&&slope(e[q1[t1]].x,e[i].x)<slope(e[q1[t1-1]].x,e[q1[t1]].x)+eps) --t1;
        q1[++t1]=i;
        while(h2<t2&&slope(e[q2[t2]].x,e[i].x)+eps>slope(e[q2[t2-1]].x,e[q2[t2]].x)) --t2;
        q2[++t2]=i;
    }for(int i=mid+1;i<=r;++i){
        if(!e[i].op) continue;
        if(e[i].x.y>0){
            while(h1<t1&&slope(e[q1[h1]].x,e[q1[h1+1]].x)<e[i].k) ++h1;
            if(h1<=t1&&dis(e[q1[h1]].x,e[i].x)>R(e[q1[h1]].x)) ans[e[i].qid]=0;
        }else{
            while(h2<t2&&slope(e[q2[t2-1]].x,e[q2[t2]].x)<e[i].k) --t2;
            if(h2<=t2&&dis(e[q2[t2]].x,e[i].x)>R(e[q2[t2]].x)) ans[e[i].qid]=0;
        }
    }cdq(mid+1,r);p1=l;p2=mid+1;
    for(int i=l;i<=r;++i)
        if(p2>r||p1<=mid&&e[p1].x.x<e[p2].x.x) tmp[i]=e[p1++];else tmp[i]=e[p2++];
    memcpy(e+l,tmp+l,sizeof(e[0])*(r-l+1));
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i){
        e[i].op=read();scanf("%lf%lf",&e[i].x.x,&e[i].x.y);e[i].id=i;
        if(e[i].op){
            e[i].qid=++tot;if(one) ans[tot]=1;
            if(e[i].x.y) e[i].k=-e[i].x.x/e[i].x.y;else e[i].k=inf;
        }else one=1;
    }sort(e+1,e+n+1,cmp);cdq(1,n);
    for(int i=1;i<=tot;++i) puts(ans[i]?"Yes":"No");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值