bzoj2961-共点圆
题目
\(n\)次操作,每次加入一个以\((x,y)\)为圆心,经过原点的圆,或者询问一个点是否在所有圆中(包括圆周)。\(n\le 5\times 10^5\)
分析
这题有两种做法,可以直接推或者运用反演。
方法一
设一个圆的圆心为\((a,b)\), 一个询问点为\((x,y)\),那么\((x,y)\)在圆内或圆周上的条件是:
\[\begin{aligned}
(x-a)^2+(y-b)^2\le a^2+b^2 \\
x^2+y^2\le 2ax+2by \\
b\ge -\frac{x}{y}a+\frac{x^2+y^2}{2y}
\end{aligned}
\]
也就是说,若一个询问点在之前的所有圆中,那么对于每个圆心\((a,b)\)都满足这条式子,而这个式子是一个明显的半平面形式,即问题变成了,每次加入圆的时候其实是加入一个圆心点,询问变成是否所有点都在一条直线的上方。我们对操作分治,变成每次把左边的点加进去,判断右边的询问是否满足。由于所有询问都是点在直线上方问题,所以我们可以对点维护下凸包,二分斜率,看看那一个点是否在下面即可。复杂度为\(O(n\log ^2 n)\) 。
方法二
所有的圆都经过同一个点——原点!考虑圆反演,那么我们会得到很多直线,一个点在圆外也就是这个点反演后在直线的上方(这题中圆心都在x轴上方)。这样问题就变成了是否每个点都被所有半平面包含。这个可以通过求半平面交再三分,二分,或排序之类的得到。复杂度也为\(O(n\log ^2 n)\),但是询问的主体是不一样的 。
代码
这是方法一的代码,方法二并没有写。
#include<bits/stdc++.h>
#define P(x) ((x)*(x))
using namespace std;
const int maxn=5e5+1;
struct node {
double x,y;
double at,dt;
inline double operator * (const node a) const {return x*a.y-y*a.x;}
inline node operator - (const node a) const {return (node){x-a.x,y-a.y};}
inline bool operator != (const node a) const {return x!=a.x || y!=a.y;}
};
struct Q {
int o;
node d;
} b[maxn];
int much[maxn];
bool ans[maxn];
node sta[maxn],bas;
int top;
double sp[maxn],at[maxn],dt[maxn];
inline double dist(node a,node b) {return P(a.x-b.x)+P(a.y-b.y);}
inline bool cmp(node a,node b) {
return a.at==b.at?a.dt<b.dt:a.at<b.at;
}
void conv(node a[],int sz) {
if (sz==1) {
sta[top=1]=a[0];
return;
}
for (int i=1;i<sz;++i) if (a[i].x<a[0].x || (a[i].x==a[0].x && a[i].y>a[0].y)) swap(a[i],a[0]);
bas=a[0];
for (int i=1;i<sz;++i) a[i].at=atan2(a[i].y-bas.y,a[i].x-bas.x),a[i].dt=dist(a[i],bas);
sort(a+1,a+sz,cmp);
sta[top=1]=a[0];
for (int i=1;i<sz;++i) if (a[i].x>=sta[top].x && a[i]!=sta[top]) {
node &nd=a[i];
while (top>1 && (nd-sta[top-1])*(sta[top]-sta[top-1])>0) --top;
sta[++top]=nd;
}
for (int i=1;i<top;++i) sp[i]=(sta[i+1].y-sta[i].y)/(sta[i+1].x-sta[i].x);
}
void solve(int l,int r) {
if (l==r) return;
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
static node a[maxn];
int m=0;
for (int i=l;i<=mid;++i) if (!b[i].o) a[m++]=b[i].d;
if (!m) return;
conv(a,m);
for (int i=mid+1;i<=r;++i) if (b[i].o) {
node &nd=b[i].d;
double k=-nd.x/nd.y;
int p=upper_bound(sp+1,sp+top,k)-sp;
ans[i]&=(P(nd.x)+P(nd.y)<=2*(sta[p].x*nd.x+sta[p].y*nd.y));
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int n;
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d%lf%lf",&b[i].o,&b[i].d.x,&b[i].d.y),much[i]=much[i-1]+(!b[i].o);
fill(ans+1,ans+n+1,true);
solve(1,n);
for (int i=1;i<=n;++i) if (b[i].o) puts(much[i] && ans[i]?"Yes":"No");
return 0;
}