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;
}

转载于:https://www.cnblogs.com/owenyu/p/7273817.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值