【题解】 保镖 半平面交
Legend
Link \(\textrm{to HOJ}\)。
(校内 OJ 题目不公开)
Editorial
这个题神仙就神仙在这里:如果你是从观测者的角度,判断这个位置合不合法,这是非常难的。
但如果你是从方向的角度来看,判断位置合不合法,就非常简单——因为这会形成一个半平面。
先不考虑方向的连续性,我们枚举一个方向 \(\theta\),计算人往这个方向走过去,能接近一队保镖的半平面是什么。
然后我们把所有半平面交起来就是答案,也就是对于所有方向都合法的那些位置。
现在的问题就是怎么把方向变成离散的。——提取出真正有有用的角度
不难发现真正有用的角度就是所有点的两两连线。
复杂度 \(O(n^3)\)。
Code
#include <bits/stdc++.h>
#define debug(...) ;//fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
freopen(#x".in" ,"r" ,stdin);\
freopen(#x".out" ,"w" ,stdout)
#define LL long long
const int MX = 100 + 3;
const LL MOD = 998244353;
const double eps = 1e-10;
int read(){
char k = getchar(); int x = 0 ,flg = 1;
while(k < '0' || k > '9') flg *= (k == '-' ? -1 : 1) ,k = getchar();
while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
return x * flg;
}
struct VECTOR{
double x ,y;
VECTOR(double X = 0.0 ,double Y = 0.0){x = X ,y = Y;}
VECTOR operator +(const VECTOR& B)const{return VECTOR(x + B.x ,y + B.y);}
VECTOR operator -(const VECTOR& B)const{return VECTOR(x - B.x ,y - B.y);}
VECTOR operator -(int)const{return VECTOR(-x ,-y);}
double operator *(const VECTOR& B)const{return x * B.y - y * B.x;}
VECTOR operator *(const double& B)const{return VECTOR(x * B ,y * B);}
VECTOR operator /(const double& B)const{return VECTOR(x / B ,y / B);}
bool operator ==(const VECTOR& B)const{return fabs(x - B.x) < eps && fabs(y - B.y) < eps;}
void output()const{debug("(%.10lf ,%.10lf)\n" ,x ,y);}
}A[MX] ,B[MX] ,inter[MX * MX];
int lcnt;
struct SEG{
VECTOR s ,d;
SEG(){s = d = VECTOR(0.0 ,0.0);}
SEG(VECTOR ST ,VECTOR END){s = ST ,d = END - ST;}
void output(){debug("(%lf ,%lf)->(%lf ,%lf)\n" ,s.x ,s.y ,(s+d).x ,(s+d).y);}
}lim[MX * MX] ,q[MX * MX];
VECTOR intersec(SEG A ,SEG B){
double s1 = A.d * (B.s - A.s);
double s2 = (B.s + B.d - A.s) * A.d;
return ((B.s + B.d) * s1 + B.s * s2) / (s1 + s2);
}
int quadrant(VECTOR A){
if(A.x >= 0 && A.y >= 0) return 1;
if(A.x <= 0 && A.y >= 0) return 2;
if(A.x <= 0 && A.y <= 0) return 3;
if(A.x >= 0 && A.y <= 0) return 4;
return 0;
}
LL sgn(double A){return fabs(A) > eps ? (A > 0 ? 1 : -1) : 0;}
bool sameDir(VECTOR A ,VECTOR B){
return sgn(A.x) == sgn(B.x) && sgn(A.y) == sgn(B.y) && fabs(A * B) < eps;
}
bool Left(SEG A ,VECTOR B){ // 查看点 B 是不是在 A 线段左侧
return A.d * (B - A.s) > eps;
}
bool Right(SEG A ,VECTOR B){
return (B - A.s) * A.d > eps;
}
bool cmp(SEG A ,SEG B){
int q1 = quadrant(A.d) ,q2 = quadrant(B.d);
if(q1 != q2) return q1 < q2;
if(A.d.x * B.d.y == A.d.y * B.d.x){
return Left(B ,A.s);
}
return A.d * B.d > 0;
}
int n ;
bool TRY(SEG T){
VECTOR test(0 ,0);
if(test == T.d) return 0;
for(int i = 1 ; i <= n ; ++i){ // 不存在一条线段完全在它右侧
if(Right(T ,A[i]) && Right(T ,B[i])) return 0;
}
return 1;
}
int main(){
__FILE(b);
n = read();
for(int i = 1 ; i <= n ; ++i){
double x1 = read() ,y1 = read() ,x2 = read() ,y2 = read();
// x1 += 1e-11 ,x2 -= 1e-11;
// y1 += 1e-11 ,y2 -= 1e-11;
A[i] = VECTOR(x1 ,y1);
B[i] = VECTOR(x2 ,y2);
}
for(int i = 1 ; i <= n ; ++i){
for(int j = 1 ; j <= n ; ++j){
SEG T;
if(TRY(T = SEG(A[i] ,B[j]))){
lim[++lcnt] = SEG(T);
T.output();
}
if(i != j && TRY(T = SEG(A[i] ,A[j]))){
lim[++lcnt] = SEG(T);
T.output();
}
if(i != j && TRY(T = SEG(B[i] ,B[j]))){
lim[++lcnt] = SEG(T);
T.output();
}
if(TRY(T = SEG(B[j] ,A[i]))){
lim[++lcnt] = SEG(T);
T.output();
}
}
}
int tmpf = 1;
std::sort(lim + 1 ,lim + 1 + lcnt ,cmp);
for(int i = 2 ; i <= lcnt ; ++i){
if(sameDir(lim[i].d ,lim[i - 1].d));
else lim[++tmpf] = lim[i];
}
lcnt = tmpf;
int head = 1 ,tail = 1;
q[1] = lim[1];
for(int i = 2 ; i <= lcnt ; ++i){
while(head < tail && !Left(lim[i] ,inter[tail - 1])) --tail;
while(head < tail && !Left(lim[i] ,inter[head])) ++head;
q[++tail] = lim[i];
if(head < tail) inter[tail - 1] = intersec(q[tail - 1] ,q[tail]);
}
while(head < tail && !Left(q[head] ,inter[tail - 1])) --tail;
if(head < tail) inter[tail] = intersec(q[head] ,q[tail]);
double Ans = 0;
if(tail - head + 1 < 3) Ans = 0;
else{
for(int i = head ; i <= tail ; ++i)
inter[i].output();
for(int i = head + 1 ; i < tail ; ++i){
Ans += (inter[i] - inter[head]) * (inter[i + 1] - inter[head]);
}
Ans = fabs(Ans) / 2;
}
printf("%.10lf\n" ,Ans);
return 0;
}