#扫描线,计算几何#洛谷 4406 [CQOI2005] 三角形面积并
分析
将三角形沿 \(x=k\) 利用扫描线分成若干个线段,那么面积并就是各个梯形的合并,求出上底与下底之和乘高除以二。
而这些线只可能是三角形的顶点或是交点,一共 \(O(n^2)\) 个,直接对这么多条关键直线求出底的长度,利用交点个数判断,
按照线段左端点从小到大排序求出当前最大右端点即可计算,时间复杂度 \(O(n^3\log n)\),也许里面难度最大的是两线段求交点吧。
代码
#include <iostream>
#include <iomanip>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=111;
typedef long double ld;
const ld COS=cos(0.626),SIN=sin(0.626);
struct Point{
ld x,y;
Point operator -(const Point &t)const{
return (Point){x-t.x,y-t.y};
}
}a[N][3];
int n,m; ld b[N*N*9],ans; pair<ld,ld>B[N];
ld cj(Point x,Point y){return x.x*y.y-x.y*y.x;}
Point intersect(int &inner,Point A,Point B,Point C,Point D){
ld S1=cj(C-A,D-A),S2=cj(D-B,C-B);
if (fabs(S1+S2)<1e-6) {inner=0; return A;}
ld ratio=S1/(S1+S2);
Point P=(Point){A.x+(B.x-A.x)*ratio,A.y+(B.y-A.y)*ratio};
if (ratio<0||ratio>1||(P.x<C.x&&P.x<D.x)||(P.x>C.x&&P.x>D.x)) inner=0;
else inner=1;
return P;
}
ld calc(ld now){
int tot=0;
for (int i=1,inner;i<=n;++i){
ld Y[3]; int TOT=0;
for (int j1=0;j1<3;++j1)
for (int j2=j1+1;j2<3;++j2){
Point P=intersect(inner,(Point){now,-1e9},(Point){now,1e9},a[i][j1],a[i][j2]);
if (inner) Y[TOT++]=P.y;
}
sort(Y,Y+TOT);
if (TOT==2&&Y[1]-Y[0]>1e-6) B[++tot]=make_pair(Y[0],Y[1]);
else if (TOT==3){
if (Y[1]-Y[0]>1e-6) B[++tot]=make_pair(Y[0],Y[1]);
else B[++tot]=make_pair(Y[1],Y[2]);
}
}
sort(B+1,B+1+tot);
ld lst=B[1].first,ans=0;
for (int i=1;i<=tot;++i){
if (B[i].second<=lst) continue;
ans+=B[i].second-max(B[i].first,lst);
lst=B[i].second;
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for (int i=1;i<=n;++i)
for (int j=0;j<3;++j){
ld x,y; cin>>x>>y;
a[i][j].x=x*COS+y*SIN;
a[i][j].y=y*COS-x*SIN;
b[++m]=a[i][j].x;
}
for (int i1=1;i1<=n;++i1)
for (int j1=0;j1<3;++j1)
for (int J1=j1+1;J1<3;++J1)
for (int i2=i1+1;i2<=n;++i2)
for (int j2=0;j2<3;++j2)
for (int J2=j2+1,inner;J2<3;++J2){
Point P=intersect(inner,a[i1][j1],a[i1][J1],a[i2][j2],a[i2][J2]);
if (inner) b[++m]=P.x;
}
sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1;
ld lst=calc(b[1]);
for (int i=2;i<=m;++i){
ld now=calc(b[i]);
ans+=(now+lst)*(b[i]-b[i-1])/2;
lst=now;
}
cout<<fixed<<setprecision(2)<<ans;
return 0;
}