校长的障碍赛

15 篇文章 0 订阅
12 篇文章 0 订阅

题目

题目描述
n n n 条线段。线段端点都是整点。找到最小的 x x x,使得去掉输入中的第 x x x 条线段后,剩下的线段两两不相交。

数据保证有解,也保证给出的 n n n 条线段至少有一个交点。

数据范围与提示
n ⩽ 1 0 5 n\leqslant 10^5 n105,端点坐标是不超过 1 0 9 10^9 109 的非负整数。

思路

要是我能找到一个交点(也就是两条相交的线段),那 答案肯定是二者之一。我只需要再判断一下,这两条线段是否与其他线段相交(不是的那一条一定只有一个交点,就是我找到的那一个交点)。

问题就是,怎么找到这一对相交的线段?

使用 扫描线!用一条平行于 y y y 轴的直线从左往右扫描,遇到左端点处理一下,遇到右端点处理一下。完结撒花!

具体是什么处理呢?

  • 左端点:将该线段加入 s e t set set,判断 它与它上面的它与它下面的 是否相交。
  • 右端点:将该线段删除,判断 它上面的与它下面的 是否相交。

什么是上面的、下面的?我们把该扫描线所截取到的每一个点画在数轴上,移动扫描线就是每一个点在左右移动(扫描线在某一线段上截取到的点,可以理解为 一个点在该线段上从左往右运动 y y y 值的变化必然是连续的)。也就是说,相邻的两个 y y y 值才可能逐渐靠近,并最终相交。所以 上下就是扫描线上的一个交点,并且这个交点恰好在当前线段的上面一个

放几张图让你们理解一下。一堆图麻烦死了。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
A A A 点、 B B B 点做的垂线的纵截距,就是对应 y y y 值。可以发现,这两个垂线是越来越靠近的,也就是纵截距越来越靠近(而且纵截距的变化是连续的,慢慢滑动)。对于 B B B 来说,它上面的点就是 A A A

由于是连续的滑动,所以只需要判断最近的两个(上面的和下面的)——在两条线段相交之前,上下关系不会改变。很明显,往下数第一条和往下数第二条不相交,即最近的那一条永远最近。要想相交,就是想要 y y y 值相同,也是先 “撞到” 最近的。用 s e t \tt set set 维护即可!到时候也好删除。

对了,删除!删除会导致它原本下面的和原本上面的相邻,就要再次检测是否相交。

由于检测相交是 O ( 1 ) \mathcal O(1) O(1) 的,所以总复杂度是 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 的。

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

struct Point{
	int x, y;
	Point(){}
	Point(int X,int Y):x(X),y(Y){}
	Point operator - (const Point &t) const {
		return Point(x-t.x,y-t.y);
	}
	int_ dot(const Point &p) const {
		return int_(x)*p.x+int_(y)*p.y;
	}
	int_ det(const Point &p) const {
		return int_(x)*p.y-int_(y)*p.x;
	}
};
# define triangle(p1,p2,p3)\
(int_(p2.x-p1.x)*(p3.y-p1.y)\
-int_(p2.y-p1.y)*(p3.x-p1.x))
int getSgn(const int_ &x){
	return (x < 0) ? -1 : (x > 0);
}

struct Line{
	Point a, b;
	bool cross(const Line &t) const {
		if((b-a).det(t.b-t.a) == 0)
			return (a-t.a).dot(b-t.a) <= 0
			|| (t.a-a).dot(t.b-a) <= 0;
		return getSgn(triangle(a,b,t.a))*
			getSgn(triangle(a,b,t.b)) <= 0
			&& getSgn(triangle(t.a,t.b,a))*
			getSgn(triangle(t.a,t.b,b)) <= 0;
	}
	bool operator < (const Line &t) const {
		return a.y < t.a.y;
	}
	double valueAt(const int &x){
		return (double(b.y)*(x-a.x)+double(a.y)*(b.x-x))/(b.x-a.x);
	}
};

const int MaxN = 100005;
Line line[MaxN];

int nowX; // current X-coordinate
struct CMP{
	bool operator()(const int &x,const int &y){
		return line[x].valueAt(nowX) < line[y].valueAt(nowX);
	}
};
tree<int,null_type,CMP,rb_tree_tag> jh; // set

struct DDG{
	int opt, id, x;
	operator int() const { return x; }
};
DDG ddg[MaxN<<1];

int main(){
	int n = readint(), L1 = 0, L2 = 0;
	for(int i=1; i<=n; ++i){
		line[i].a.x = readint();
		line[i].a.y = readint();
		line[i].b.x = readint();
		line[i].b.y = readint();
		if(line[i].a.x > line[i].b.x)
			swap(line[i].a,line[i].b);
		ddg[i<<1].opt = 1;
		ddg[i<<1].x = line[i].a.x;
		ddg[2*i-1].opt = -1;
		ddg[2*i-1].x = line[i].b.x;
		ddg[i<<1].id = ddg[2*i-1].id = i;
	}
	sort(ddg+1,ddg+(n<<1)+1);
	for(int i=1; i<=(n<<1); ++i){
		nowX = ddg[i].x; // important
		if(ddg[i].opt == 1){
			auto it = jh.lower_bound(ddg[i].id);
			if(it != jh.end()){
				L1 = ddg[i].id, L2 = *it; // expect
				if(line[L1].cross(line[L2])) break;
			}
			if(it != jh.begin()){
				L1 = ddg[i].id, L2 = *(-- it);
				if(line[L1].cross(line[L2])) break;
			}
			jh.insert(ddg[i].id);
		}
		if(ddg[i].opt == -1){
			jh.erase(ddg[i].id);
			auto it = jh.lower_bound(ddg[i].id);
			if(it != jh.end() && it != jh.begin()){
				L1 = *it; L2 = *(-- it); // expect
				if(line[L1].cross(line[L2])) break;
			}
		}
	}
	if(L1 > L2) swap(L1,L2);
	bool ok = true;
	rep(i,1,n) if(i != L1 && i != L2)
		if(line[i].cross(line[L2])) ok = 0;
	printf("%d\n",ok ? L1 : L2);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值