假设当前询问点为$(A,B)$,那么它在一个以$(x,y)$为圆心的圆里需要满足:
$(x-A)^2+(y-B)^2leq x^2+y^2$
$2Ax+2Bygeq A^2+B^2$
等价于询问所有圆心与$(2A,2B)$的点积的最小值是否小于$A^2+B^2$。
考虑将修改操作二进制分组,分成$O(log n)$段连续的修改区间,每一段建立上下凸壳维护,查询时在凸壳上三分。
时间复杂度$O(nlog^2n)$,常数很小。
#include<cstdio> #include<algorithm> #define N 500010 int n,m,op,t,q[30],r1[30],r2[30],flag;double A,B,C,D; struct P{double x,y;}a[N],b[N],q1[N],q2[N]; inline bool cmp1(const P&a,const P&b){return a.x==b.x?a.y>b.y:a.x<b.x;} inline bool cmp2(const P&a,const P&b){return a.x==b.x?a.y<b.y:a.x<b.x;} inline void update(){ while(t&&m-q[t]==q[t]-q[t-1])t--;q[++t]=m; int i,cnt=0,L=q[t-1]+1,R; for(i=L;i<=m;i++)b[cnt++]=a[i]; std::sort(b,b+cnt,cmp1); for(q1[R=L]=b[0],i=1;i<cnt;q1[++R]=b[i++])while(R>L&&(q1[R].y-q1[R-1].y)*(b[i].x-q1[R].x)<=(b[i].y-q1[R].y)*(q1[R].x-q1[R-1].x))R--; r1[t]=R; std::sort(b,b+cnt,cmp2); for(q2[R=L]=b[0],i=1;i<cnt;q2[++R]=b[i++])while(R>L&&(q2[R].y-q2[R-1].y)*(b[i].x-q2[R].x)>=(b[i].y-q2[R].y)*(q2[R].x-q2[R-1].x))R--; r2[t]=R; } inline double mul(const P&b){return A*b.x+B*b.y;} inline void ask1(int l,int r){ int m1,m2;double s1,s2; while(l<=r){ int len=(r-l)/3; if((s1=mul(q1[m1=l+len]))<(s2=mul(q1[m2=r-len]))){ if(s1<C){flag=1;return;} r=m2-1; }else{ if(s2<C){flag=1;return;} l=m1+1; } } } inline void ask2(int l,int r){ int m1,m2;double s1,s2; while(l<=r){ int len=(r-l)/3; if((s1=mul(q2[m1=l+len]))<(s2=mul(q2[m2=r-len]))){ if(s1<C){flag=1;return;} r=m2-1; }else{ if(s2<C){flag=1;return;} l=m1+1; } } } inline void ask(){ flag=0; for(int i=1;i<=t;i++){ if(B<0)ask1(q[i-1]+1,r1[i]);else ask2(q[i-1]+1,r2[i]); if(flag)return; } } int main(){ scanf("%d",&n); while(n--){ scanf("%d%lf%lf",&op,&A,&B);A+=D,B+=D; if(!op)a[++m].x=A,a[m].y=B,update(); else{ if(!m){puts("No");continue;} C=A*A+B*B,A+=A,B+=B,ask(); if(flag)puts("No");else puts("Yes"),D++; } } return 0; }