Code[VS]1302 小矮人

15 篇文章 0 订阅
7 篇文章 0 订阅

原题链接:http://codevs.cn/problem/1302/

小矮人

题目描述 Description

矮人们平时有走亲访友的习惯。一天,矮人国要修一条高速公路,矮人们希望他们走亲访友的时候,能够不必穿越高速公路,这样会更安全一些。现在有M个高速公路的修建方案,请你判断这M条高速功能是否能满足矮人们的期望。也就是说给出平面上的N个点(矮人们的住所位置),对于M条直线(高速公路),依次判断这N个点是否在每条直线的同一侧。是输出GOOD,不是输出BAD。

N,M≤100000

输入描述 Input Description

第一行一个整数N,表示矮人的住所数。

接下来N行每行一个坐标代表矮人的住所坐标。

接下来的若干行(到文件结尾)每行4个整数,代表高速公路上的2个点。

所有坐标均在-1e9到1e9之间

输出描述 Output Description

对合法的方案输出GOOD,否则输出BAD。

样例输入 Sample Input

4
0.0 0
6.00 -0.001
3.125 4.747
4.747 0.47
5 3 7 0
4 -4.7 7 4.7
4 47 4 94

样例输出 Sample Output

GOOD
BAD
BAD

题解

首先,如果公路穿过了矮人国,那么直线肯定穿过了凸包,更进一步,该直线肯定穿过凸包的最远点对之间,我们只需要求该直线的最远点对。
显然,夹角最接近且大于该直线夹角的线段起点是一个最远点,而另一个最远点则是该直线夹角取反后,夹角最接近且大于的线段起点。而我们可以很容易的利用求出的凸包的栈中的元素得到一个夹角单调二分查找。  

代码
#include<bits/stdc++.h>
#define db double
#define eps 1e-8 
using namespace std;
const int M=1e5+5;
const db Pi=acos(db(-1));
const db esp=1e-10;
struct pt{db x,y;};
pt operator +(pt a,pt b){return (pt){a.x+b.x,a.y+b.y};}
pt operator -(pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
bool operator < (const pt &a,const pt &b){return (a.x==b.x)?(a.y<b.y):(a.x<b.x);}
db operator *(pt a,pt b){return a.x*b.y-a.y*b.x;}
pt operator *(pt a,db b){return (pt){a.x*b,a.y*b};}
pt operator /(pt a,db b){return (pt){a.x/b,a.y/b};}
bool ab(pt a,pt b){return a*b<-eps;}
db area(pt a,pt b,pt c){return abs((b-a)*(c-a)/2.0);}
double dis(pt a,pt b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
pt x[M],sta[M];
db ang[M];
int top;
int n,m;
void tubao()
{
    sort(x+1,x+n+1);
    for(int i=1;i<=n;++i)
    {
        while(top>1&&(sta[top]-sta[top-1])*(x[i]-sta[top-1])<=0) --top;
        sta[++top]=x[i];
    }
    int k=top;
    for(int i=n-1;i>=1;--i)
    {
        while(top>k&&(sta[top]-sta[top-1])*(x[i]-sta[top-1])<=0) --top;
        sta[++top]=x[i];
    }
}
void in()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%lf%lf",&x[i].x,&x[i].y);
}
db angle(pt hh)
{
    db v=atan2(hh.y,hh.x);
    return v<-Pi/2?v+2*Pi:v;
}
int cmp1(db x)
{
    if(fabs(x)<esp) return 0;
    return x<0?-1:1;
}
bool cmp2(const db &a,const db &b)
{
    if(cmp1(b-a)==1) return 1;
    return 0;
}
bool check(pt a,pt b)
{
    if(n<=1) return 1;
    pt u=sta[upper_bound(ang+1,ang+top,angle(b-a),cmp2)-ang];
    pt v=sta[upper_bound(ang+1,ang+top,angle(a-b),cmp2)-ang];
    if(cmp1(((b-a)*(u-a))*((b-a)*(v-a)))<eps) return 0;
    return 1;
}
void ac()
{
    db a1,a2,b1,b2;
    for(int i=1;i<top;++i) ang[i]=angle(sta[i+1]-sta[i]);
    while(scanf("%lf%lf%lf%lf",&a1,&a2,&b1,&b2)==4)
    check((pt){a1,a2},(pt){b1,b2})?printf("GOOD\n"):printf("BAD\n");
}
int main()
{
    in();tubao();ac();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值