BZOJ 4140: 共点圆加强版 [二进制分组][凸包]

题意

支持在平面直角坐标系内进行操作:

1.加入一个圆心在 (x,y) 且过原点的圆

2.询问点 (x,y) 是否被之前加入的所有圆包含(在圆内或圆心上)

强制在线

题解

询问即判断 (Xxi)2+(Yyi)2x2i+y2i 是否成立

整理一下式子,得 X/Yxi+(X2+Y2)/(2Y)yi

其实左边就是一条直线

k=X/Y,b=(X2+Y2)/(2Y)

即所有圆心要在直线 y=kx+b 上方,所以我们可以维护一个下凸包来做

动态凸包太难写,可以用二进制分组(不知道是啥的可以参考xhr的答辩论文)了

对于本题只要对圆心分组后维护 log2n 个凸包,分组改变时直接暴力重构凸包

查询判断的时候二分逼近斜率,找到凸包上那条斜率最接近 k <script type="math/tex" id="MathJax-Element-26">k</script>的直线然后算一下截距即可

#include<cstdio>
#include<algorithm>
#define N 500005
using namespace std;

int n,num,top,cnt,L[N],R[N];
struct node{
    double x,y;
    node(){}
    node(double a,double b):x(a),y(b){}
    bool operator < (const node &a) const {
        if(x==a.x) return y<a.y;
        else return x<a.x;
    }
}p[N],s[N];

inline double dir(node a,node b,node c){
    return (c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);
}

void insert(node v){
    p[++cnt]=v;
    L[++top]=cnt;
    while(top>1&&cnt-L[top]+1==L[top]-L[top-1]) --top;
    sort(p+L[top],p+cnt+1);
    int nw=L[top];s[nw]=p[nw];
    for(int i=L[top]+1;i<=cnt;++i){
        while(nw>L[top]&&dir(s[nw-1],s[nw],p[i])>=0) --nw;
        s[++nw]=p[i];
    }
    R[top]=nw;
}

inline int find(int l,int r,double k){
    int mid,res,h=l;
    while(l<=r){
        mid=l+r>>1;
        if(mid==h||k>(s[mid].y-s[mid-1].y)/(s[mid].x-s[mid-1].x)) res=mid,l=mid+1;
        else r=mid-1;
    }
    return res;
}

bool query(double x,double y){
    if(!top) return top;
    double a=(x*x+y*y)/(2*y),b=-x/y;
    for(int i=1;i<=top;++i){
        int pos=find(L[i],R[i],b);
        if(a+b*s[pos].x>s[pos].y) return 0;
    }
    return 1;
}

int main(){
    freopen("testdata.in","r",stdin);
    scanf("%d",&n);
        while(n--){
        int opt;double x,y;
        scanf("%d%lf%lf",&opt,&x,&y);
        x+=num,y+=num;
        if(!opt) insert((node){x,y});
        else{
//          ++x,++y;
            if(query(x,y)) ++num,puts("Yes");else puts("No");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值