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;
}