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

对于给出的一个圆心 (xi,yi) ( x i , y i ) ,在它内部点 (x,y) ( x , y ) 需满足

(xxi)2+(yyi)2x2i+y2ix2+y22xxi+2yyiyixyxi+x2+y22y ( x − x i ) 2 + ( y − y i ) 2 ≤ x i 2 + y i 2 ⇔ x 2 + y 2 ≤ 2 x x i + 2 y y i ⇔ y i ≥ − x y x i + x 2 + y 2 2 y

问题转化为插入点,询问给出一条直线,问是否所有点都在这条线上方。

可以离线用 CDQ C D Q 分治,但这题强制在线,所以二进制分组就好啦。

二进制分组,大概是当满足贡献独立,而且为了询问所构建的结构不太好插入的时候,采取分组建,暴力重构的方法。可以解决部分强制在线的问题。

#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#define _X first
#define _Y second
#define mp(x,y) make_pair(x,y)
#define Pairdd pair<double,double>
#define Vec_Pairdd vector< pair<double,double> >
using namespace std;
const int maxn=1000005;
int numG,sz[maxn];
Vec_Pairdd G[maxn],tmp;

bool check(Pairdd A,Pairdd B,Pairdd C){
  return (B._Y-A._Y)*(C._X-B._Y)>(C._Y-B._Y)*(B._X-A._X);
}
void Merge_top(){
  tmp.clear(); Vec_Pairdd &A=G[numG-1], &B=G[numG];
  int p1=0,p2=0,szA=A.size(),szB=B.size();
  for(int i=1;i<=szA+szB;i++)
    if(p1<szA&&(p2==szB||A[p1]<B[p2]))
      tmp.push_back(A[p1++]); else tmp.push_back(B[p2++]);
  A.clear(); B.clear(); numG--; sz[numG]<<=1; sz[numG+1]=0;
  for(int i=0;i<tmp.size();i++){
    while(A.size()>=2&&check(A[A.size()-2],A[A.size()-1],tmp[i])) A.pop_back();
    A.push_back(tmp[i]);
  }
}
void Maintain(){
  while(numG>1&&sz[numG]==sz[numG-1]) Merge_top();
}
void Insert(double x,double y){
  G[++numG].push_back(mp(x,y)); sz[numG]=1;
  Maintain();
}
bool Query(int id,double k,double b){
  Vec_Pairdd &A=G[id];
  int L=0,R=(int)A.size()-2,res=0;
  while(L<=R){
    int mid=(L+R)>>1;
    if((A[mid+1]._Y-A[mid]._Y)>k*(A[mid+1]._X-A[mid]._X))
      R=mid-1, res=mid; else L=mid+1;
  }
  return A[res]._Y-(k*A[res]._X+b)>=-1e-7;
}
bool Query(double k,double b){
  if(!numG) return false;
  for(int i=1;i<=numG;i++) if(!Query(i,k,b)) return false;
  return true;
}
int Q,num_yes;
int main(){
  freopen("bzoj4140.in","r",stdin);
  freopen("bzoj4140.out","w",stdout);
  scanf("%d",&Q);
  while(Q--){
    int _type; double x,y;
    scanf("%d%lf%lf",&_type,&x,&y); x+=num_yes; y+=num_yes;
    if(_type==0){
      Insert(x,y);
    } else{
      if(Query(-x/y,(x*x+y*y)/(y*2))) num_yes++, puts("Yes"); else puts("No");
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值