[扫描线 计算几何] BZOJ 1845 [Cqoi2005] 三角形面积并

%%%PoPoQQQ:http://blog.csdn.net/popoqqq/article/details/42581881


经典的扫描线

首先求出所有直线交点的横坐标,排序,去重

然后对于每个横坐标,两段之间夹的部分一定是一个或多个梯形

因此我们取中位线,求出中位线被所有三角形覆盖区间的区间并的长度,即可计算出这部分的面积


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define eps 1e-7
using namespace std;

inline int dcmp(double a,double b){
	if (fabs(a-b)<eps) return 0;
	if (a>b) return 1;
	return -1;
}

struct Point{
	double x,y;
	Point(double x=0,double y=0):x(x),y(y) { }
	void read(){
		scanf("%lf%lf",&x,&y);
	}
};

struct Line{
	Point A,B;
	double k,b;
	Line(Point a,Point b):A(a),B(b) { }
	Line(double x1=0,double y1=0,double x2=0,double y2=0){
		A.x=x1; A.y=y1; B.x=x2; B.y=y2;
	}
	bool dir() {
		return dcmp(A.x,B.x)==0;
	}
	bool calc(){
		if (dir()) return 0;
		k=(B.y-A.y)/(B.x-A.x);
		b=B.y-k*B.x;
		return 1;
	}
}L[305];

inline double Cross(Point p1,Point p2,Point p0)
{
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

inline double Pd(Line L1,Line L2){
	return dcmp(Cross(L1.A,L1.B,L2.A)*Cross(L1.A,L1.B,L2.B),0)<0 && dcmp(Cross(L2.A,L2.B,L1.A)*Cross(L2.A,L2.B,L1.B),0)<0;
}

inline bool Sub(Line L1,Line L2,Point &t){
	if (!Pd(L1,L2)) return 0;
	if (L1.dir() && L2.dir()) return 0;
	if (L1.dir())
		t=Point(L1.A.x,L2.k*L1.A.x+L2.b);
	else if (L2.dir())
		t=Point(L2.A.x,L1.k*L2.A.x+L1.b);
	else 
	{
		if (dcmp(L1.k,L2.k)==0) return 0;
		t.x=(L2.b-L1.b)/(L1.k-L2.k);
		t.y=t.x*L1.k+L1.b;
	}
	return 1;
}


int n,m;
double sx[150005];
int icnt;

struct abcd{
	double y;
	int f;
	abcd(double y=0,int f=0):y(y),f(f) { }
	bool operator < (const abcd &B) const{
		return dcmp(y,B.y)==0?f>B.f:y<B.y;
	}
}data[505];
int cnt;

double Ans;

int main()
{
	Point a,b,c;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	scanf("%d",&n);
	for (int i=n;i;i--)
	{
		a.read(),b.read(),c.read(); 
		if (Cross(a,b,c)==0) { n--; continue; } 
		sx[++icnt]=a.x; sx[++icnt]=b.x; sx[++icnt]=c.x; 
		L[++m]=Line(a,b); if (!L[m].dir()) L[m].calc();
		L[++m]=Line(a,c); if (!L[m].dir()) L[m].calc();
		L[++m]=Line(b,c); if (!L[m].dir()) L[m].calc();
	}
	for (int i=1;i<=m;i++)
		for (int j=i+1;j<=m;j++)
			if (Sub(L[i],L[j],a))
				sx[++icnt]=a.x;
	sort(sx+1,sx+icnt+1);
	int pos=0;
	for (int i=1;i<=icnt;i++)
		if (i==1 || dcmp(sx[i],sx[i-1]))
			sx[++pos]=sx[i];
	icnt=pos;
	for (int i=2;i<=icnt;i++)
	{
		double Lx=sx[i-1],Rx=sx[i],Mx=(Lx+Rx)/2,lasty;
		int lastf;
		cnt=0;
		for (int i=1;i<=n;i++)
		{
			double yy[5]={0}; int ic=0;
			for (int j=3*i-2;j<=3*i;j++)
				if (dcmp(L[j].A.x,Mx)*dcmp(L[j].B.x,Mx)<=0)
					yy[++ic]=L[j].k*Mx+L[j].b;
			if (ic)
			{
				if (ic==3 && dcmp(yy[1],yy[2])==0) swap(yy[2],yy[3]);
				if (dcmp(yy[1],yy[2])>0) swap(yy[1],yy[2]); 
				data[++cnt]=abcd(yy[1],1);
				data[++cnt]=abcd(yy[2],-1);
			}
		}
		sort(data+1,data+cnt+1);
		
		for (int i=1,j,itmp;i<=cnt;i=j+1)
		{
			itmp=data[i].f;
			for (j=i;j+1<=cnt && itmp;) itmp+=data[++j].f;
			Ans+=(data[j].y-data[i].y)*(Rx-Lx);
		}
	}
	printf("%.2lf\n",Ans-eps);
	return 0;
}


你可能感兴趣的:([扫描线 计算几何] BZOJ 1845 [Cqoi2005] 三角形面积并)