其实也不是很难,但是没考虑周全调了半天……
我还是tcltcltcl
显然可以直接对圆反演后用平衡树维护动态凸包
考虑到对于一个点(x0,y0),(x_0,y_0),(x0,y0),在圆(x,y)(x,y)(x,y)中即(x0−x)2+(y0−y)2≤x2+y2(x_0-x)^2+(y_0-y)^2\le x^2+y^2(x0−x)2+(y0−y)2≤x2+y2
化简后即x02+y02−2x0x≤2y0yx_0^2+y_0^2-2x_0x\le 2y_0yx02+y02−2x0x≤2y0y
当y0>0y_0>0y0>0时
−x0y0x+x02+y022y0≤y-\frac{x_0}{y_0}x+\frac {x_0^2+y_0^2} {2y_0}\le y−y0x0x+2y0x02+y02≤y
y0<0y_0<0y0<0的情况类似
发现左边就是一个直线的方程,问题就转化成了判断所有点是否处于这条直线的某一边
发现我们只需要在凸包上二分找到两边斜率卡的那个点判断一下就可以了
对2种情况分别维护一下下凸包和上凸包就可以了
复杂度O(nlog2n)O(nlog^2n)O(nlog2n)
其实可以归并排序和对询问的斜率归并排序做到O(nlogn)O(nlogn)O(nlogn)的
但是结构体太丑常数太大也懒得写了
注意特判y0=0y_0=0y0=0的情况
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=500005;
const double eps=1e-9;
const double inf=1e20;
struct point{
double x,y;
point(double _x=0,double _y=0):x(_x),y(_y){}
friend inline point operator +(const point &a,const point &b){
return point(a.x+b.x,a.y+b.y);
}
friend inline point operator -(const point &a,const point &b){
return point(a.x-b.x,a.y-b.y);
}
friend inline double operator *(const point &a,const point &b){
return a.x*b.y-a.y*b.x;
}
friend inline double getk(const point &a,const point &b){
return fabs(a.x-b.x)<=eps?inf:(a.y-b.y)/(a.x-b.x);
}
inline double dis(){
return x*x+y*y;
}
}q[N],q1[N],q2[N];
struct ask{
point p;double k,b;
inline void read(){
scanf("%lf%lf",&p.x,&p.y);
}
int op,id,ans;
}p[N],tmp[N];
inline double P(double x){
return x*x;
}
inline bool comp(const ask&a,const ask&b){
return a.id<b.id;
}
int n,tot,cnt1,cnt2;
inline bool checkup(const point &a){
double k=-a.x/a.y,b=(P(a.x)+P(a.y))/(2*a.y);
int l=1,r=cnt1,res=l;
while(l<=r){
int mid=(l+r)>>1;
if(k-getk(q1[mid+1],q1[mid])>-eps)r=mid-1,res=mid;
else l=mid+1;
}
return (q1[res].x*k+b)-q1[res].y>-eps;
}
inline bool checkdown(const point &a){
double k=-a.x/a.y,b=(P(a.x)+P(a.y))/(2*a.y);
int l=1,r=cnt2,res=r;
while(l<=r){
int mid=(l+r)>>1;
if(k-getk(q2[mid+1],q2[mid])<eps)r=mid-1,res=mid;
else l=mid+1;
}
return (q2[res].x*k+b)-q2[res].y<eps;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
double mx=-inf,mn=inf;
cdq(l,mid),cdq(mid+1,r);
tot=0,cnt1=0,cnt2=0;
for(int i=l;i<=mid;i++){
if(p[i].op==0)q[++tot]=p[i].p,mx=max(mx,p[i].p.x),mn=min(mn,p[i].p.x);
}
q1[cnt1=1]=q[1],q2[cnt2=1]=q[1];
for(int i=2;i<=tot;i++){
while(cnt1>=2&&getk(q[i],q1[cnt1-1])-getk(q1[cnt1],q1[cnt1-1])>-eps)cnt1--;
q1[++cnt1]=q[i];
}
for(int i=2;i<=tot;i++){
while(cnt2>=2&&getk(q[i],q2[cnt2-1])-getk(q2[cnt2],q2[cnt2-1])<eps)cnt2--;
q2[++cnt2]=q[i];
}
for(int i=mid+1;i<=r;i++){
point a=p[i].p;
if(p[i].op){
if(a.y<-eps){
if(!checkup(a))p[i].ans=0;
}
else if(a.y>eps){
if(!checkdown(a))p[i].ans=0;
}
}
}
for(int i=1;i<=cnt1;i++)q1[i]=point(0,0);
for(int i=1;i<=cnt2;i++)q2[i]=point(0,0);
cnt1=l,cnt2=mid+1,tot=l-1;
while(cnt1<=mid&&cnt2<=r){
if(p[cnt1].p.x<p[cnt2].p.x)tmp[++tot]=p[cnt1++];
else tmp[++tot]=p[cnt2++];
}
while(cnt1<=mid)tmp[++tot]=p[cnt1++];
while(cnt2<=r)tmp[++tot]=p[cnt2++];
for(int i=l;i<=r;i++){
p[i]=tmp[i];
}
}
bool flag=0;
int main(){
n=read();double mx=-1e9,mn=1e9;
for(int i=1;i<=n;i++){
p[i].op=read(),p[i].id=i,p[i].read();
point a=p[i].p;
if(p[i].op==0)flag=1,mx=max(mx,a.x),mn=min(mn,a.x);
else {
p[i].ans=flag;
if(fabs(a.y)<eps){
if(a.x>eps&&mn*2-a.x<eps)p[i].ans=0;
if(a.x<-eps&&mx*2-a.x>-eps)p[i].ans=0;
}
}
}
cdq(1,n);
sort(p+1,p+n+1,comp);
for(int i=1;i<=n;i++){
if(p[i].op==1){
if(p[i].ans)puts("Yes");
else puts("No");
}
}
}