牛客小白月赛3 J-零点 (计算几何)

题目链接

思路:中文题目,题面很清晰。这个题目有几个讨论点需要注意一下。

1、如果两端的射线至少一条与x轴重合了,那么铁定是无穷多个零点了,直接输出-1

2、两端射线单独讨论:如果定义射线的两个端点的纵坐标不同号,那么一定跟x轴有交点

3、讨论中间的线段:如果线段与x轴重合,那么就从左端点遍历到右端点每个加入答案;如果相交就把交点横坐标加入答案

4、我使用set来存答案(自动排序+去重),当size > 3e5 的时候就直接输出-1

5、注意判断交点是否为整数,用强制转换后的数与原数作比较

6、虽然坐标没有超过int值,但是交点会超,记得long long

#include <bits/stdc++.h>
using namespace std;

const double eps = 1e-15;

int sgn(double x) {
	if(fabs(x) < eps) return 0;
	if(x < 0) return -1;
	else return 1;
}

struct Point { // 表示点
	double x, y;
	Point(){}
	Point(double _x,double _y) {
		x = _x; y = _y;
	}
	Point operator + (const Point& b) const {
		return Point(x + b.x,y + b.y);
	}
	Point operator - (const Point &b) const {
		return Point(x - b.x,y - b.y);
	}
	double operator * (const Point &b) const {
		return x*b.x + y*b.y;
	}
	double operator ^ (const Point &b) const {
		return x*b.y - y*b.x;
	}
};

struct Line { // 表示线段
	Point s,e;
	Line(){}
	Line(Point _s,Point _e) {
		s = _s;e = _e;
	} 
	// 判断直线相交的,无需要可以不用(相交返回交点的版本,下面有一个只做判断的版本)
	// 返回pair first为0重合 为1平行 为2相交且second为交点
	pair <int, Point> operator & (const Line &b)const { 
		Point res = s;
		if(sgn((s-e)^(b.s-b.e)) == 0) {
			if(sgn((b.s-s)^(b.e-s)) == 0)
				return make_pair(0,res);//两直线重合
			else return make_pair(1,res);//两直线平行
		}
		double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x += (e.x - s.x)*t;
		res.y += (e.y - s.y)*t;
		return make_pair(2,res);//有交点
	}
};

bool inter(Line l1,Line l2) {
	return
	max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
	max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
	max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
	max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
	sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
	sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}

int n;
Point p[700005];
Line l[700005];

bool iszheng(double x) {
	long long wtf = x;
	if(sgn(x-wtf) == 0) return 1;
	return 0;
}

int main() {
	while(~scanf("%d", &n)) {
		int numl = 0;
		for(int i = 0;i < n; i++) {
			scanf("%lf %lf", &p[i].x, &p[i].y);
			if(i) l[numl++] = Line(p[i-1], p[i]);
		}
		
		set <long long> st; st.clear();
		Line x = Line(Point(0,0), Point(1,0));
		
		// 射线与x轴重合
		if((l[0] & x).first == 0 || (l[numl-1] & x).first == 0) {
			puts("-1");
			continue;
		}
		
		// 第一条射线判断
		if((p[1].y > p[0].y && p[1].y > 0) || (p[1].y < p[0].y && p[1].y < 0)) {
			Point t = (l[0] & x).second;
			double res = t.x;
			if(iszheng(res)) st.insert((long long)res);
		}
        
        // 最后一条射线判断
        if((p[n-1].y > p[n-2].y && p[n-2].y < 0) || (p[n-1].y < p[n-2].y && p[n-2].y > 0)) {
            Point t = (l[numl-1] & x).second;
            double res = t.x;
            if(iszheng(res)) st.insert((long long)res);
        }
		
		bool ok = 1;
		for(int i = 1;i < numl-1; i++) {
			// 线段与x轴重合的情况
			if((l[i] & x).first == 0) {
				for(long long j = l[i].s.x; j <= l[i].e.x; j++) {
					st.insert(j);
					if(st.size() > 300000) {ok = 0; break;} 
				}
				if(!ok) break;
			}
			else {
				// 线段与x轴相交
				Line xx = Line(Point(l[i].s.x, 0), (Point(l[i].e.x, 0)));
				if(inter(l[i], xx)) {
					Point t = (l[i] & xx).second;
					double res = t.x;
					if(iszheng(res)) st.insert((long long)res);
				}
			}
			
			if(st.size() > 300000) break;
		}
		
		if(st.size() > 300000) puts("-1");
		else {
			printf("%d\n", (int)st.size());
			for(long long i: st) printf("%lld ", i);
		}
		puts("");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值