CSA Empty Triangles

Empty Triangles

Description

给出第一象限上的 M M 个固定点,接着给出K次询问,每次询问四个整数 (x1,y1,x2,y2) ( x 1 , y 1 , x 2 , y 2 ) 表示询问以 (0,0),(x1,y1),(x2,y2) ( 0 , 0 ) , ( x 1 , y 1 ) , ( x 2 , y 2 ) 为顶点的三角形内是否有固定点。

Data Constraints

0x1,y1,x2,y2109 0 ≤ x 1 , y 1 , x 2 , y 2 ≤ 10 9   M,K105     M , K ≤ 10 5
保证所有固定点不会在任何三角形的边界上。

Solution

将所有的固定点按照极角序排序。
考虑用线段树维护向左下角偏的凸壳,线段树区间 [l,r] [ l , r ] 维护编号为 l r l   r 的固定点组成的向左下角偏的凸壳,显然 [l,r] [ l , r ] 的凸壳可以 O(rl+1) O ( r − l + 1 ) 线性得到。
接着用二分求出夹在向量 (x1,y1) ( x 1 , y 1 ) 和向量 (x2,y2) ( x 2 , y 2 ) 之间的固定点的区间,接着在 log(n) l o g ( n ) 个凸壳上二分出那个最优的点,再看看是否在直线下面即可。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=22e4,M=N<<2,K=M*20;

ll x[N],y[N],a[K],b[K];

int be[M],en[M],bh[N];
ll xx[K],yy[K];
ll d[M][2],e[M][2];

int n,q,ok,p;
ll a1,b1,a2,b2;

bool kmp(int x,int y)
{return (a[x]*a[y]==0)?(a[x]!=a[y]?(a[x]<a[y]):b[x]>b[y]):(b[x]*b[y]==0)?(b[x]!=b[y]?(b[x]>b[y]):a[x]<a[y]):(b[x]*a[y]>b[y]*a[x]);}

inline bool judge1(int x,int y)
{return (a[x]*a[y]==0)?(a[x]!=a[y]?(a[x]<a[y]):b[x]>b[y]):(b[x]*b[y]==0)?(b[x]!=b[y]?(b[x]>b[y]):a[x]<a[y]):(b[x]*a[y]>b[y]*a[x]);}

inline ll cj(ll x1,ll y1,ll x2,ll y2)
{return x1*y2-y1*x2;}

inline bool judge2(ll x,ll y)
{return cj(a2-a1,b2-b1,x-a1,y-b1)<0;}

inline void build(int o,int l,int r)
{
    if(l==r){
        be[o]=en[o]=++p;
        a[p]=x[l]; b[p]=y[l];
        return;
    }
    int mid=l+r>>1;
    build(o<<1,l,mid); build((o<<1)^1,mid+1,r);
    int i=be[o<<1],j=be[(o<<1)^1],ii=en[o<<1],jj=en[(o<<1)^1],v=0;
    while(i<=ii||j<=jj)if(j>jj||kmp(i,j)&&i<=ii){
        d[++v][0]=a[i];
        d[v][1]=b[i]; ++i;
    }else{
        d[++v][0]=a[j];
        d[v][1]=b[j]; ++j;
    }

    int u=0;
    fo(i,1,v){
        ll r1=d[i][0],r2=d[i][1];
        while(u>1&&cj(r1-e[u-1][0],r2-e[u-1][1],e[u][0]-e[u-1][0],e[u][1]-e[u-1][1])>=0)
        --u;
        e[++u][0]=r1; e[u][1]=r2;
    }
    be[o]=p+1; en[o]=p+u;
    fo(i,1,u)a[i+p]=e[i][0],b[i+p]=e[i][1];
    p=en[o];
}

inline void find(int o,int l,int r,int le,int ri)
{
    if(l==le&&r==ri){
        if(be[o]+1>=en[o])fo(i,be[o],en[o])ok=ok|(judge2(a[i],b[i]));
        else{
            int zb=be[o],yb=en[o];
            ok=ok|judge2(a[zb],b[zb]);
            ok=ok|judge2(a[yb],b[yb]);
            ++zb; 
            while(zb+1<yb){
                int mid=zb+yb>>1;
                ll d1=cj(a2-a1,b2-b1,a[mid]-a[mid-1],b[mid]-b[mid-1]);
                ll d2=cj(a2-a1,b2-b1,a[mid+1]-a[mid],b[mid+1]-b[mid]);
                if((d2>0)&&d1>=0)yb=mid;else zb=mid;
            }
            ok=ok|judge2(a[zb],b[zb]);
        }
        return;
    }
    int mid=l+r>>1;
    if(ri<=mid)find(o<<1,l,mid,le,ri);
    else if(le>mid)find((o<<1)^1,mid+1,r,le,ri);
    else{
        find(o<<1,l,mid,le,mid);
        if(ok)return;
        find((o<<1)^1,mid+1,r,mid+1,ri);
    }
}

int main()
{
    scanf("%d%d",&n,&q);
    fo(i,1,n)scanf("%lld%lld",&a[i],&b[i]);
    fo(i,1,n)bh[i]=i;
    sort(bh+1,bh+n+1,kmp);
    fo(i,1,n)x[i]=a[bh[i]],y[i]=b[bh[i]];
    build(1,1,n);
    fo(i,1,q){
        scanf("%lld%lld%lld%lld",&a1,&b1,&a2,&b2);
        if(b1*a2<a1*b2||b1==0)swap(a1,a2),swap(b1,b2);
        int l1=0,r1=n+1;
        while(l1+1<r1){
            int mid=l1+r1>>1;
            if(b1*x[mid]>=a1*y[mid])r1=mid;else l1=mid;
        }
        int l2=0,r2=n+1;
        while(l2+1<r2){
            int mid=l2+r2>>1;
            if(y[mid]*a2>=x[mid]*b2)l2=mid;else r2=mid;
        }
        if(r1>l2)puts("N");
        else{
            ok=judge2(x[l2],y[l2]);
            find(1,1,n,r1,l2);
            if(ok)puts("Y");else puts("N");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值