bzoj2961 共点圆

http://www.elijahqi.win/2018/02/10/bzoj2961/

Description

  在平面直角坐标系中,Wayne需要你完成n次操作,操作只有两种:
1.0 x y。表示在坐标系中加入一个以(x, y)为圆心且过原点的圆。
2.1 x y。表示询问点(x, y)是否在所有已加入的圆的内部(含圆周),且至少在一个圆内部(含圆周)。
为了减少你的工作量,题目保证圆心严格在x轴上方(纵坐标为正),且横坐标非零。
Input

  第1行一个整数n。
接下来n行,每行第一个数是0或1,分别表示两种操作。
接着有两个实数x和y,具体意义见题面。
Output

  对于每个询问操作,如果点在所有已加入的圆内(或圆周上),则输出“Yes”(不含引号);否则输出“No”(不含引号)。
Sample Input
5
0 2.0000 3.0000
0 4.0000 1.0000
1 1.000000 1.000000
0 -3.0000 2.0000
1 1.000000 1.000000
Sample Output
Yes
No
HINT

对于100%的数据,n≤500000,所有坐标绝对值不超过10000。

输入数据保证圆心纵坐标为正,横坐标非零。

圆心坐标保留4位小数,询问点坐标保留6位小数,请选手注意控制精度。

Source

中国国家队清华集训 2012-2013 第二天
满足条件的圆点不妨设为x0,y0 然后我每次询问的点不妨认为是x,y
那么显然满足一个条件 (xx0)2+(yy0)2<=x02+y02 ( x − x 0 ) 2 + ( y − y 0 ) 2 <= x 0 2 + y 0 2
通过化简可以得到 y0>=xyx0+x2+y22y y 0 >= − x y x 0 + x 2 + y 2 2 ∗ y
或者 y0<=xyx0+x2+y22y y 0 <= − x y x 0 + x 2 + y 2 2 ∗ y 所以我们一共需要维护两个凸包一个上凸 一个下凸每次我首先把所有询问的斜率排序 然后按照时间分治 每次先把时间靠前的扔前面 然后构造两个凸包 每次询问的时候 根据询问点的y的大小选择在某一个凸包上贰分一下 贰分出来的结果就是可以看图来想象 相当于我是这个凸包上多个半平面组成一个交 然后求一下我询问我的半平面交是否在询问直线的上方 在cdq的实现上还有一些细节 相当于我首先按照时间排序 然后做好之后 按照x排序方便我下一层排序 这就要求我们必须先分治左边 然后统计完这一层的答案再去分治右边才okay

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 550000
#define eps 1e-10
#define inf 1e40
using namespace std;
struct node{
    double x,y,k;int op,id,qid;
}qr[N],tmp[N];
inline double slope(node a,node b){
    if (abs(a.x-b.x)<eps) return inf;
    return (b.y-a.y)/(b.x-a.x);
}
inline bool cmp(const node &a,const node &b){return a.k<b.k;}
bool ans[N];
int q1[N],q2[N];
inline double sqr(double x){return x*x;}
inline double dis(node a,node b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
inline double R(node a){return sqr(a.x)+sqr(a.y);}
inline void print(int l,int r){
    printf("%d %d\n",l,r);
    for (int i=l;i<=r;++i){
        puts("op x y qid k");
        printf("%d %lf %lf %d %lf\n",qr[i].op,qr[i].x,qr[i].y,qr[i].qid,qr[i].k);
    }
}
inline void cdq(int l,int r){
    if (l==r) return;int mid=l+r>>1;
    int nowl=l,nowr=mid+1;
    for (int i=l;i<=r;++i) 
        if (qr[i].id<=mid) tmp[nowl++]=qr[i];else tmp[nowr++]=qr[i];
    memcpy(qr+l,tmp+l,sizeof(qr[0])*(r-l+1));cdq(l,mid);int l1=1,r1=0,l2=1,r2=0;
    for (int i=l;i<=mid;++i){
        if (qr[i].op) continue;
        while(l1<r1&&slope(qr[q1[r1-1]],qr[q1[r1]])-slope(qr[q1[r1]],qr[i])>eps) --r1;
        q1[++r1]=i;
        while(l2<r2&&slope(qr[q2[r2-1]],qr[q2[r2]])-slope(qr[q2[r2]],qr[i])<-eps) --r2;
        q2[++r2]=i;
    }
    for (int i=mid+1;i<=r;++i){
        if (!qr[i].op) continue;
        if (qr[i].y>0){
            while(l1<r1&&slope(qr[q1[l1]],qr[q1[l1+1]])<qr[i].k) ++l1;
            if (l1<=r1&&dis(qr[i],qr[q1[l1]])>R(qr[q1[l1]])) ans[qr[i].qid]=0; 
        }
        if (qr[i].y<0){
            while(l2<r2&&slope(qr[q2[r2-1]],qr[q2[r2]])<qr[i].k) --r2;
            if (l2<=r2&&dis(qr[i],qr[q2[r2]])>R(qr[q2[r2]])) ans[qr[i].qid]=0;
        }
    }cdq(mid+1,r);nowl=l,nowr=mid+1;
    for (int i=l;i<=r;++i)
        if (nowl<=mid&&qr[nowl].x<qr[nowr].x||nowr>r) tmp[i]=qr[nowl++];else tmp[i]=qr[nowr++];
    memcpy(qr+l,tmp+l,sizeof(qr[0])*(r-l+1));//print(l,r);
}
int n;
int main(){
    freopen("bzoj2961.in","r",stdin);
    scanf("%d",&n);int cnt=0,e=0;            
    for (int i=1;i<=n;++i){
        scanf("%d%lf%lf",&qr[i].op,&qr[i].x,&qr[i].y);qr[i].id=i;
        if (qr[i].op) {qr[i].qid=++cnt;if (e) ans[cnt]=1;if (qr[i].y) qr[i].k=-qr[i].x/qr[i].y;else qr[i].k=inf;}
        else e=1;
    }sort(qr+1,qr+n+1,cmp);cdq(1,n);
    for (int i=1;i<=cnt;++i) ans[i]?puts("Yes"):puts("No");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值