【题解】 保镖 半平面交

Legend

Link ( extrm{to HOJ})

(校内 OJ 题目不公开)

Editorial

这个题神仙就神仙在这里:如果你是从观测者的角度,判断这个位置合不合法,这是非常难的。

但如果你是从方向的角度来看,判断位置合不合法,就非常简单——因为这会形成一个半平面。

先不考虑方向的连续性,我们枚举一个方向 ( heta),计算人往这个方向走过去,能接近一队保镖的半平面是什么。

然后我们把所有半平面交起来就是答案,也就是对于所有方向都合法的那些位置。

现在的问题就是怎么把方向变成离散的。——提取出真正有有用的角度

不难发现真正有用的角度就是所有点的两两连线。

复杂度 (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)
" ,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)
" ,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
" ,Ans);

	return 0;
}
原文地址:https://www.cnblogs.com/imakf/p/14449423.html