[SCOI 2015集训-2015.4.16]Problem B(计算几何+极角序二分)

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 210000
#define EPS 1e-8

using namespace std;

typedef long long int LL;

struct Point
{
    LL x,y;
    Point(){}
    Point(double _x,double _y):x(_x),y(_y){}
}polygon[MAXN];

bool operator==(Point a,Point b)
{
    return a.x==b.x&&a.y==b.y;
}

Point operator-(Point a,Point b)
{
    return Point(a.x-b.x,a.y-b.y);
}

LL cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

int dcmp(double x)
{
    if(fabs(x)<EPS) return 0;
    if(x>EPS) return 1;
    return -1;
}

struct Info
{
    double ang;
    int id;
}info[MAXN];

bool cmp(Info a,Info b)
{
    return a.ang<b.ang;
}

bool onLeft(Point A,Point B,Point x) //判断x是否在向量A->B左边
{
    if(x==A||x==B) return true;
    return cross(B-A,x-A)>=0;
} 

int main()
{
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    int n,q;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%I64d%I64d",&polygon[i].x,&polygon[i].y);
        info[i].ang=atan2(polygon[i].y,polygon[i].x);
        info[i].id=i;
    }
    sort(info+1,info+n+1,cmp);
    int cnt=0;
    LL lastx,lasty;
    bool inPoly=false;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        LL x,y;
        scanf("%I64d%I64d",&x,&y);
        if(i!=1)
        {
            if(inPoly)
            {
                x=lastx+x;
                y=lasty+y;
            }
            else
            {
                x=lastx-x;
                y=lasty-y;
            }
        }
        lastx=x,lasty=y;
        if(x==0&&y==0) //特判在原点,此时没极角
        {
            inPoly=true;
            cnt++;
            continue;
        } 
        double ang=atan2(y,x);
        if(dcmp(ang-info[1].ang)<=0||dcmp(ang-info[n].ang)>=0) //特判当前询问点夹在第1个点和第n个点极角线之间 
        {
            if(onLeft(polygon[info[n].id],polygon[info[1].id],Point(x,y)))
            {
                inPoly=true;
                cnt++; 
            } 
            else inPoly=false;
            continue;
        }
        int lowerBound=1,upperBound=n-1,ans=0;
        while(lowerBound<=upperBound) //二分出在从(0,0)射到(x,y)的有向直线右边的最靠近的那个点 
        {
            int mid=(lowerBound+upperBound)>>1;
            if(dcmp(ang-info[mid].ang)>=0)
            {
                ans=mid;
                lowerBound=mid+1;
            }
            else upperBound=mid-1;
        }
        if(onLeft(polygon[info[ans].id],polygon[info[ans+1].id],Point(x,y)))
        {
            inPoly=true;
            cnt++; 
        } 
        else inPoly=false;
    }
    printf("%d\n",cnt);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值