传送门biu~
把每个边看成一个向量,向量左边为指定区域。用向量求交点,用向量判断一个点是否在这条向量的左面。先在一个单调队列中不断加元素,最后再删队首队尾。注意细节。
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-9;
struct point{
double x,y;
point(double _=0,double __=0){x=_;y=__;}
point operator +(const point&r)const{return point(x+r.x,y+r.y);}
point operator -(const point&r)const{return point(x-r.x,y-r.y);}
double operator *(const point&r)const{return x*r.y-y*r.x;}
point operator *(const double&r)const{return point(x*r,y*r);}
}p[55];
struct line{
point p,v;
double alpha;
bool operator< (const line&r)const{return alpha<r.alpha;}
line() {}
line(point a,point b){
p=a;
v=b-a;
alpha=atan2(v.y,v.x);
}
point operator^ (const line&r) const{
point re=p-r.p;
double tmp=r.v*re/(v*r.v);//我把这写成了tmp=re*r.v/(v*r.v)就tmd调了一天
return p+v*tmp;
}
}lines[550],stac[550];
int tp,head=1,tail;
bool onleft(point p,line l){
point tmp=p-l.p;
return l.v*tmp>=0;
}
void get_half_plane_intersection(){
sort(lines+1,lines+tp+1);
for(int i=1;i<=tp;++i){
while(tail>=2 && !onleft(stac[tail]^stac[tail-1],lines[i])) --tail;
if(tail>=1 && fabs(stac[tail].v*lines[i].v)<=eps){ //平行
if(onleft(lines[i].p,stac[tail]))stac[tail]=lines[i];
}
else stac[++tail]=lines[i];
}
while(1){
if(tail-head+1>=2 && !onleft(stac[tail]^stac[tail-1],stac[head])) --tail;
else if(tail-head+1>=2 && !onleft(stac[head]^stac[head+1],stac[tail])) ++head;
else break;
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
int m;
scanf("%d",&m);
for(int j=1;j<=m;++j){
scanf("%lf%lf",&p[j].x,&p[j].y);
if(j!=1) lines[++tp]=line(p[j-1],p[j]);
}
lines[++tp]=line(p[m],p[1]);
}
get_half_plane_intersection();
if(tail-head+1<=2) printf("0.000");
else{
int sum=0;
for(int i=head+1;i<=tail;++i) p[++sum]=stac[i-1]^stac[i];
p[++sum]=stac[tail]^stac[head];
double ans=0;
for(int i=2;i<=sum;++i) ans+=p[i-1]*p[i];
ans+=p[sum]*p[1];
printf("%.3lf",ans/2);
}
return 0;
}