2019.03.27【SCOI2015】【洛谷P4250】【BZOJ4445】【LOJ2008】小凸想跑步(半平面交)

洛谷传送门

BZOJ传送门

LOJ传送门


解析:

其实这道题本身并不毒瘤,但是我看了一下其他题解,发现有的代码实现是真的复杂。。。

这道题其实可以完全不用三角函数做(但是我半平面交习惯先求积极角后排序就用了atan2)。

大胆猜想这种求满足某种条件的区域面积的问题就是半平面交了。

显然我们可以枚举多边形的每条边与第一条边进行比较,显然令两个三角形面积相等的点是有轨迹的,我们尝试求出这个轨迹,靠近第一条边的一边就是合法区域。

如果这个轨迹是直线我们就可以上半平面交了。

现在考虑点 P 1 , P 2 , P i , P i + 1 P_1,P_2,P_{i},P_{i+1} P1,P2,Pi,Pi+1,动点为 P P P,接下来的 x , y x,y x,y如果有下标则表示对应点的 x , y x,y x,y,如果没有则表示 P P P的。

我们要求的区域即 ( P 1 − P ) × ( P 2 − P ) ≤ ( P i − P ) × ( P i + 1 − P ) (P_1-P)\times(P_2-P)\leq (P_i-P)\times (P_{i+1}-P) (P1P)×(P2P)(PiP)×(Pi+1P)

暴力拆开,合并同类项(这些我就不写了,太长了)之后得到:

P 1 × P 2 − P i × P i + 1 ≤ x ( y i − y i + 1 − y 1 + y 2 ) − y ( x i − x i + 1 − x 1 + x 2 ) P_1\times P_2-P_i\times P_{i+1}\leq x(y_i-y_{i+1}-y_1+y_2)-y(x_i-x_{i+1}-x_1+x_2) P1×P2Pi×Pi+1x(yiyi+1y1+y2)y(xixi+1x1+x2)

将常量全部求出后我们要求的实际上就是这个东西:

c ≤ a x + b y c\leq ax+by cax+by

想必来写这道题的各位都知道这个怎么转成半平面。

把所有限制和多边形的边加入半平面集后做半平面交就好了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
	}
	
	inline int getint(){
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))f^=c=='-';re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

using std::cout;
using std::cerr;

cs int N=1e5+5; 
cs double eps=1e-9;
inline int sign(cs double &x){return (int)(x>-eps)-(x<eps);}
struct Point{
	double x,y;
	Point(){}
	Point(cs double &_x,cs double &_y):x(_x),y(_y){}
	friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
	friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
	friend Point operator*(cs Point &a,cs double &b){return Point(a.x*b,a.y*b);}
	friend double operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
}p[N];

struct Line{
	Point s,e;
	double rad;
	Line(){}
	Line(cs Point &a,cs Point &b):s(a),e(b),rad(atan2(e.y-s.y,e.x-s.x)){}
	friend bool operator<(cs Line &a,cs Line &b){
		return sign(a.rad-b.rad)?sign(a.rad-b.rad)<0:sign((a.e-a.s)*(b.e-a.s))>0;
	}
}l[N<<1],q[N<<1];

inline Point inter(cs Line &a,cs Line &b){
	double s1=(a.e-a.s)*(b.s-a.s),s2=(b.e-a.s)*(a.e-a.s);
	double k=s1/(s1+s2);
	return b.s+(b.e-b.s)*k;
}

inline bool judge(cs Line &a,cs Line &b,cs Line &c){
	return sign((c.e-c.s)*(inter(a,b)-c.s))<0;
}

int n,m,tot,head,tail;

inline void Half_plane_Inter(){
	std::sort(l+1,l+m+1);n=0;
	for(int re i=1;i<=m;++i){
		if(i==1||sign(l[i].rad-l[i-1].rad))++n;
		l[n]=l[i];
	}
	m=n;
	q[head=1]=l[1];
	q[tail=2]=l[2];
	for(int re i=3;i<=m;++i){
		while(head<tail&&judge(q[tail-1],q[tail],l[i]))--tail;
		while(head<tail&&judge(q[head+1],q[head],l[i]))++head;
		q[++tail]=l[i];
	}
	while(head<tail&&judge(q[tail-1],q[tail],q[head]))--tail;
	while(head<tail&&judge(q[head+1],q[head],q[tail]))++head;
	n=0;
	q[tail+1]=q[head];
	for(int re i=head;i<=tail;++i)p[++n]=inter(q[i],q[i+1]);
}

double all,area;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i)p[i].x=getint(),p[i].y=getint();
	p[n+1]=p[1];
	for(int re i=1;i<=n;++i){
		l[++m]=Line(p[i],p[i+1]);
		all+=p[i]*p[i+1];
		if(i>1){
			double a=p[1]*p[2]-p[i]*p[i+1];
			double b=p[i].y-p[i+1].y-p[1].y+p[2].y;
			double c=p[i+1].x-p[i].x-p[2].x+p[1].x;
			//a<=bx+cy
			Point st=Point(sign(c)?0:a/b,sign(c)?a/c:0);
			l[++m]=Line(st,st+Point(c,-b));
		}
	}
	Half_plane_Inter();
	p[n+1]=p[1];
	for(int re i=1;i<=n;++i)area+=p[i]*p[i+1];
	printf("%.4f",fabs(area/all));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值