【CodeVS 1302】小矮人

传送门


problem

也就是说给出平面上的 n n n 个点,对于 m m m 条直线,依次判断这 n n n 个点是否在每条直线的同一侧。是输出 GOOD,不是输出 BAD。

数据范围: n , m ≤ 100000 n,m≤100000 n,m100000


solution

直线与凸包关系的模板题。

可以把凸包每条线的 a t a n 2 atan2 atan2 的值记下来,然后二分找与直线相切的两个点(为了保证单调性,凸包的第一个点选 y y y 最大,且在 y y y 最大时 x x x 最小的点),然后用叉积判一下即可。

注意题目中 n = 0 n=0 n=0 的数据。


code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define eps 1e-8
using namespace std;
int n,top;
double angle[N];
int sgn(double x)  {return x>eps?1:(x<-eps?-1:0);}
struct point{
	double x,y;
	point(double _x=0,double _y=0):x(_x),y(_y){}
	friend point operator+(const point &a,const point &b)  {return point(a.x+b.x,a.y+b.y);}
	friend point operator-(const point &a,const point &b)  {return point(a.x-b.x,a.y-b.y);}
	friend double dot(const point &a,const point &b)  {return a.x*b.x+a.y*b.y;}
	friend double cross(const point &a,const point &b)  {return a.x*b.y-a.y*b.x;}
}p[N],stk[N];
bool operator<(const point &a,const point &b){
	double now=cross(a-p[1],b-p[1]);
	return sgn(now)==0?a.x<b.x:sgn(now)>0;
}
void Graham(){
	int pos=1;
	for(int i=2;i<=n;++i)
		if(p[pos].y<p[i].y||(p[pos].y==p[i].y&&p[pos].x>p[i].x))
			pos=i;
	swap(p[pos],p[1]),sort(p+2,p+n+1);
	stk[++top]=p[1],stk[++top]=p[2];
	for(int i=3;i<=n;++i){
		while(top>1&&sgn(cross(p[i]-stk[top-1],stk[top]-stk[top-1]))>0)  top--;
		stk[++top]=p[i];
	}
	stk[top+1]=stk[1];
	for(int i=1;i<=top;++i)
		angle[i]=atan2(stk[i+1].y-stk[i].y,stk[i+1].x-stk[i].x);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%lf%lf",&p[i].x,&p[i].y);
	if(n!=0)  top=0,Graham();
	point S,T;
	while(~scanf("%lf%lf%lf%lf",&S.x,&S.y,&T.x,&T.y)){
		if(n<=1)  {puts("GOOD");continue;}
		double A1=atan2(T.y-S.y,T.x-S.x),A2=atan2(S.y-T.y,S.x-T.x);
		point u=stk[lower_bound(angle+1,angle+top+1,A1)-angle];
		point v=stk[lower_bound(angle+1,angle+top+1,A2)-angle];
		puts(sgn(cross(S-u,T-u)*cross(S-v,T-v))<=0?"BAD":"GOOD");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值