题面描述
传送门
思路
先看一下SCY的视频吧,讲得真的很清(luo)楚(suo)。
忽略掉一些奇奇怪怪的语言
首先我们先来了解一下SCY开头说的
ax+by+c=0
也就是下面这条直线?
我稍微纳闷了一会,这不是
y=kx+b吗?
稍微转换一下?
ax+by+c=0
by=−ax−c
y=−bax−bc
这不就出来了吗。
这个东西我们以后会用到的。
atan函数就是已知边长,逆向求近似度数的一个玄学函数。
就如下图:
设
tanθ=−7.993.49,atan(3.49,−7.99)=θ
特别地,
θ是弧度制
void get_angle(){angle=atan2(p2.y-p1.y,p2.x-p1.x);}
这句话实际上的意思就为将这条有向的线段当作向量(其实就是向量),将起点挪到原点处,求出它的
atan值。
之后我们用
atan来排序
bool cmp(segment sg1,segment sg2)
{
if(sg1.angle<sg2.angle)return true;
}
这个排序实际上是将线段(直线)的方向有序化了,大概是这样一个形状(此处是已经经过挪移的形状):
此处按字典序小的排在前面为排序后的结果。
那么有一些特殊的情况,
比如
两条带方向的直线平行(图中仅给出线段,因为可以从线段中推出直线):
注意这里的平行实际意义上为
sg1.angle==ag2.angle。
此时我们应该选择
p1还是
p2呢,
选择
p1,因为
p2箭头半平面∪p1箭头左边的半平面=p1箭头左边的半平面
我们要求是半平面的交集。
fabs(sg1.angle-sg2.angle)<eps
判断是否平行
pd(sg1.p1,sg2)
判断
sg1是否在
sg2左面
那么现在
p1的位置先于
p2的位置。
完整的cmp
bool cmp(segment sg1,segment sg2)
{
if(sg1.angle<sg2.angle)return true;
if(fabs(sg1.angle-sg2.angle)<eps&&pd(sg1.p1,sg2))return true;
return false;
}
接下来就
pd函数:
bool pd(point p,segment sg)//是否在此直线左侧
{
if(mul(p,sg.p2,sg.p1)<=eps)return true;//叉乘之积为负数即为左侧
return false;
}
个人认为
jd是通篇最难的一个函数了。
先讲讲被SCY蓝掉的那个
jd吧
因为:
它要推柿子
我们要用到上面的结论直线
ax+by+c=0。
首先我们要解一个解析式方程组求半平面交点
{a1x+b1y+c1=0a2x+b2y+c2=0
我们要求的是
x,y,
首先我们看看怎么求一组
(a1,b1,c1)吧。
首先
p1,p2点在
a1x+b1y+c1=0这条直线上了。
故
{a1p1.x+b1p1.y+c1=0⋯①a1p2.x+b1p2.y+c1=0⋯②
①式-②式,得
a1(p1.x−p2.x)+b1(p1.y−p2.y)=0
发现这是一个不定方程。
求特解
解得:
a1=p1.y−p2.y,b1=p2.x−p1.x
正好满足。
将
a1,b1代入①式就有:
p1.x∗p1.y−p1.x∗p2.y+p2.x∗p1.y−p1.x∗p1.y+c1=0
p2.x∗p1.y−p1.x∗p2.y+c1=0
c1=p1.x∗p2.y−p2.x∗p1.y
a2,b2,c2同理可得。
a2=p3.y−p4.y,b2=p4.x−p3.x,c2=p3.x∗p4.y−p4.x∗p3.y
之后就是解方程了
{a1x+b1y+c1=0a2x+b2y+c2=0
{a1x+b1y=−c1a2x+b2y=−c2
{a1x=−c1−b1ya2x=−c2−b2y
{x=−a1c1+b1∗yx=−a2c2+b2∗y
a1c1+b1∗y=a2c2+b2∗y
(c1+b1∗y)∗a2=(c2+b2∗y)∗a1
c1∗a2+a2∗b1∗y=c2∗a1+a1∗b2∗y
(a2∗b1−a1∗b2)y=c2∗a1−c1∗a2
y=a2∗b1−a1∗b2c2∗a1−c1∗a2
同理
x=a2∗b1−a1∗b2c1∗b2−c2∗b1
终于写完一个了。
SCY标程中的那个其实是更加简化的一个东西:
x=a2∗b1−a1∗b2c1∗b2−c2∗b1=(p3.y−p4.y)∗(p2.x−p1.x)−(p1.y−p2.y)∗(p4.x−p3.x)(p1.x∗p2.y−p2.x∗p1.y)∗(p4.x−p3.x)−(p3.x∗p4.y−p4.x∗p3.y)∗(p2.x−p1.x)
=p2.x∗p3.y−p1.x∗p3.y−p2.x∗p4.y+p1.x∗p4.y−p4.x∗p1.y+p3.x∗p1.y−p4.x∗p2.y+p3.x∗p2.y(p1.x∗p2.y−p2.x∗p1.y)∗(p4.x−p3.x)−(p3.x∗p4.y−p4.x∗p3.y)∗(p2.x−p1.x)
=((p1.x−p3.x)∗(p4.y−p3.y)−(p4.x−p3.x)∗(p1.y−p3.y))−((p2.x−p3.x)∗(p4.y−p3.y))−(p4.x−p3.x)(p2.y−p3.y)(p1.x∗p2.y−p2.x∗p1.y)∗(p4.x−p3.x)−(p3.x∗p4.y−p4.x∗p3.y)∗(p2.x−p1.x)
=mul(p1,p4,p3)−mul(p2,p4,p3)mul(p1,p4,p3)∗p2.x−mul(p2,p4,p3)∗p1.x
同理,
y=mul(p1,p4,p3)−mul(p2,p4,p3)mul(p1,p4,p3)∗p2.y−mul(p2,p4,p3)∗p1.y
很具体有木有?
接下来就是比较主要的部分了。
对于新加进来的一个半平面,
我们会出现以下的情况:
!pd(jd(sglist[tail],sglist[tail-1]),seg[i]))
也就是
segtail−1,segtail+1的交点在新半平面交(直线方向左边)外,如图:
2.同样的,也有:
所以我们要开一个类似单调队列的队列,来维护这个半平面交。
特别地,在所有线段都被遍历过之后,我们还需要:
while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),sglist[head]))--tail;
这是因为:
接下来就是求交点了。
pl=0;for(int i=head+1;i<=tail;i++)plist[++pl]=jd(sglist[i-1],sglist[i]);
plist[++pl]=jd(sglist[head],sglist[tail]);
最后喜闻乐见求面积。
对了这道题还有边界。
SCY讲得还行。
就不赘叙了。
AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define eps 1e-8
using namespace std;
const int N=2e4+10;
const double Maxn=1e5;
const double Minn=-1e5;
struct point{double x,y;}plist[N];int pl;
struct segment
{
point p1,p2;
double angle;
segment(){}
segment(double x1,double y1,double x2,double y2)
{p1.x=x1,p1.y=y1;p2.x=x2,p2.y=y2;angle=atan2(p2.y-p1.y,p2.x-p1.x);}
}sglist[N],seg[N];int n,head,tail;
double mul(point p1,point p2,point p0)
{
double x1=p1.x-p0.x,x2=p2.x-p0.x;
double y1=p1.y-p0.y,y2=p2.y-p0.y;
return x1*y2-x2*y1;
}
point jd(segment sg1,segment sg2)
{
double t1=mul(sg1.p1,sg2.p2,sg2.p1);
double t2=mul(sg1.p2,sg2.p2,sg2.p1);
point p1=sg1.p1,p2=sg1.p2,p;
p.x=(t1*p2.x-t2*p1.x)/(t1-t2);
p.y=(t1*p2.y-t2*p1.y)/(t1-t2);
return p;
}
bool pd(point p,segment sg){return mul(p,sg.p2,sg.p1)<=eps;}
bool cmp(segment sg1,segment sg2)
{
if(sg1.angle<sg2.angle)return true;
if(fabs(sg1.angle-sg2.angle)<eps&&pd(sg1.p1,sg2))return true;
return false;
}
void SI()
{
sort(seg+1,seg+n+1,cmp);
int tp=1;for(int i=2;i<=n;i++)if(seg[i].angle-seg[tp].angle>eps)seg[++tp]=seg[i];
n=tp;head=1;tail=2;
sglist[1]=seg[1];sglist[2]=seg[2];
for(int i=3;i<=n;i++)
{
while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),seg[i]))--tail;
while(head<tail&&!pd(jd(sglist[head],sglist[head+1]),seg[i]))++head;
sglist[++tail]=seg[i];
}
while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),sglist[head]))--tail;
if(tail-head+1<=2){puts("0.0");return ;}
pl=0;for(int i=head+1;i<=tail;i++)plist[++pl]=jd(sglist[i-1],sglist[i]);
plist[++pl]=jd(sglist[head],sglist[tail]);
double ans=0;for(int i=3;i<=pl;i++)ans+=mul(plist[i-1],plist[i],plist[1]);
printf("%.1lf\n",fabs(ans)/2.0);
}
int main()
{
scanf("%d",&n);
seg[1]=segment(Maxn,Maxn,Minn,Maxn);
seg[2]=segment(Minn,Maxn,Minn,Minn);
seg[3]=segment(Minn,Minn,Maxn,Minn);
seg[4]=segment(Maxn,Minn,Maxn,Maxn);
for(int i=1;i<=n;i++)
{
double x1,y1,x2,y2;scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
seg[i+4]=segment(x1,y1,x2,y2);
}
n+=4;
SI();
return 0;
}