CF 1388 Codeforces Round #660 (Div. 2)

E

题意:给n( n ≤ n\leq n 2000)条水平线段,求一个方向向量,使得这些直线按照该方向向量向x轴做投影后,所有线段不相交,求这些线段所覆盖的位置的最左端的和最右端的距离最小。
在这里插入图片描述
思路
  考虑两条线段AB、CD,只有AC、BD才可能成为答案。同时,斜率处于AC与BD之间的向量不能作为答案,否则会使AB、CD的投影相交。
  所以,我们需要枚举出所有可能的答案,然后用区间覆盖的方式除去矛盾的线段,然后再比较哪一个向量求得的答案最小的答案。
  对于一个已知向量,我们需要求出投影后最左端和最右端的点。可以建凸包,然后根据叉积(或者斜率)进行二分即可。
  注意,可能所有直线都在同一高度,此时需要特判垂直的情况(见样例3)。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long double LD;
typedef long long LL;
const LD inf = 1e18;
const int N = 2010;
struct LINE{
	LL xl, xr, y;
	bool operator < (const LINE & other)
	const{ return y < other.y;}
}line[N];
struct POINT{
	LL x, y;
	POINT(LL a = 0, LL b = 0) {x = a; y = b;}
	POINT operator - (const POINT  & b){return POINT (x - b.x, y - b.y);}
}pt[N * 2];
LL X(POINT a, POINT b)	{	return a.x * b.y - a.y * b.x;}
struct QU{
	LL x, y, xx, yy;
	int opt;
	QU(LL a = 0, LL b = 0, LL c = 0, LL d = 0, int e = 0) {x = a; y = b; xx = c; yy = d; opt = e;}
	bool operator < (const QU & b) const{ return X(POINT(xx - x, yy - y), POINT(b.xx - b.x, b.yy - b.y)) > 0 || X(POINT(xx - x, yy - y), POINT(b.xx - b.x, b.yy - b.y)) == 0 && opt < b.opt;}
}qu[N * N];
int que[N * 2], vist[N * N];
int top, tot, mid_top;
LL dist(POINT x, POINT y)
{
	return (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);
} 
int cmp(POINT x, POINT y)
{	
	return X(x - pt[1], y - pt[1]) > 0 || (X(x - pt[1], y - pt[1]) == 0 && dist(x, pt[1]) < dist(y, pt[1]));
}
void get_convex()
{
	int k = 1;
	for(int i = 1; i <= tot; i++)
		if(pt[i].y < pt[k].y || (pt[i].y == pt[k].y && pt[i].x < pt[k].x))
			k = i;
	swap(pt[k], pt[1]);
	sort(pt + 2, pt + tot + 1, cmp);
	que[1] = 1;
	top = 1;
	for(int i = 2; i <= tot; i++)
	{
		while(top > 1 && X(pt[que[top]] - pt[que[top - 1]], pt[i] - pt[que[top - 1]]) <= 0)	top--;
		que[++top] = i;
	}
	que[top + 1] = 1;
	mid_top = 1; 
	for(; mid_top <= top; mid_top++)
		if(pt[que[mid_top]].y > pt[que[mid_top + 1]].y)
			break;
}
LD get_l(int id)
{
	int l = mid_top;
	int r = top + 1;
	while(r - l > 1)
	{
		int mid = (l + r) >> 1;
		if(X(pt[que[mid + 1]] - pt[que[mid]], POINT(qu[id].x - qu[id].xx, qu[id].y - qu[id].yy)) >= 0)	l = mid;
		else	r = mid;
	}
	return min((LD)pt[que[l]].x - (LD)((qu[id].xx - qu[id].x) * pt[que[l]].y) / (LD) (qu[id].yy - qu[id].y),(LD)pt[que[r]].x - (LD)((qu[id].xx - qu[id].x) * pt[que[r]].y) / (LD) (qu[id].yy - qu[id].y));
}
LD get_r(int id)
{
	int l = 1;
	int r = mid_top + 1;
	while(r - l > 1)
	{
		int mid = (l + r) >> 1;
		if(X(pt[que[mid + 1]] - pt[que[mid]], POINT(qu[id].xx - qu[id].x, qu[id].yy - qu[id].y)) >= 0)		l = mid;
		else	r = mid;
	}
	return max((LD)pt[que[l]].x - (LD)((qu[id].xx - qu[id].x) * pt[que[l]].y) / (LD) (qu[id].yy - qu[id].y),(LD)pt[que[r]].x - (LD)((qu[id].xx - qu[id].x) * pt[que[r]].y) / (LD) (qu[id].yy - qu[id].y));
}
int cmp1(LINE x, LINE y) {	return x.xl < y.xl;}
LD solve_RT(int n)
{
	sort(line + 1, line + n + 1, cmp1);
	LL now = line[1].xr;
	for(int i = 2; i <= n; i++)
	{
		if(line[i].xl < now)	return inf;
		now = line[i].xr;
	}
	return (LD)(now - line[1].xl);
} 
int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%lld%lld%lld", &line[i].xl, &line[i].xr, &line[i].y);
		pt[++tot] = POINT(line[i].xl, line[i].y);
		pt[++tot] = POINT(line[i].xr, line[i].y);
	}
	LD ans = inf;
	ans = solve_RT(n);
	sort(line + 1, line + n + 1);
	int cnt = 0;
	for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++)
		{
			if(line[i].y == line[j].y)	continue;
			qu[++cnt] = QU(line[i].xl, line[i].y, line[j].xr, line[j].y, 1);
			qu[++cnt] = QU(line[i].xr, line[i].y, line[j].xl, line[j].y, -1);
		}
	sort(qu + 1, qu + cnt + 1);
	int now = 0;
	for(int i = 1; i <= cnt; i++)
	{
		if(qu[i].opt == 1)
		{
			if(now > 0)	vist[i] = 1;
			now++;
		}
		else
		{
			now--;
			if(now > 0)	vist[i] = 1;
		}
	get_convex();
	for(int i = 1; i <= cnt; i++)
		if(!vist[i])
		{
			LD l = get_l(i);
			LD r = get_r(i);
			ans = min(ans, r - l);
		}
	printf("%.9Lf",ans);
	return 0;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值