2019.12.22日常总结兼扫描线略讲

洛谷P1884

【题意】:

Farmer John has purchased a new machine that is capable of planting grass within any rectangular region of his farm that is "axially aligned" (i.e., with vertical and horizontal sides). Unfortunately, the machine malfunctions one day and plants grass in not one, but N (1 <= N <= 1000) different rectangular regions, some of which may even overlap.

Given the rectangular regions planted with grass, please help FJ compute the total area in his farm that is now covered with grass.

在一个笛卡尔平面坐标系(即平面直角坐标系)里(则X轴向右是正方向,Y轴向上是正方向),有 N ( 1 ≤ N ≤ 1 × 1 0 3 ) N(1\leq N\leq1 \times 10^3) N(1N1×103)个矩形,第 i i i个矩形的左上角坐标是 ( x 1 , y 1 ) , (x_1, y_1), (x1,y1)右下角坐标是 ( x 2 , y 2 ) (x_2,y_2) x2,y2。问这 N N N个矩形所覆盖的面积是多少?注意:被重复覆盖的区域的面积只算一次。

2019.12.22日常总结兼扫描线略讲_第1张图片


【思路】: 扫描线的模板题。

首先,我们把矩阵的坐标离散化,然后把矩阵放到一个虚拟的平面直角坐标系中。

假设我们有一条线,从X轴的负方向向正方向不断的移动。我们就可以利用它来计算答案:很简单,它扫到多少面积就加上多少面积。

现在我们考虑如何保存矩阵。我们把一个矩阵拆开,只留下它的左右两条边,且左边的权为 1 1 1,右边的权为 − 1 -1 1

所以在我们的先移动的时候,就可以把边的权放入一个线段树中,这样就可以既快速又方便的计算答案了。

注意答案要用long long

【代码】:

const int N=2010;
struct node{
	int x,y0,y1,c;
//	bool operator < (node t) const{
//		return x
//	}
}a[N];//map H;
int n,m,qy[N];long long ans;
inline bool cmp(node a,node b){
	return a.x<b.x;
}
struct segment_tree{
	int L[N<<2],R[N<<2],Len[N<<2],C[N<<2];
	void init(int o,int l,int r){
		L[o]=l;R[o]=r;C[o]=Len[o]=0;
		if (l!=r){
			register int mid=(l+r)>>1;
			init(o<<1,l,mid);
			init(o<<1|1,mid+1,r);
		}
	}
	void add(int o,int l,int r,int v){
		if (r<L[o]||l>R[o]) return;
		if (l<=L[o]&&R[o]<=r) C[o]+=v;
		else{
			add(o<<1,l,r,v);
			add(o<<1|1,l,r,v);
		}
		if (C[o]) Len[o]=qy[R[o]+1]-qy[L[o]];
		else if (L[o]==R[o]) Len[o]=0;//这句话非常重要!!!
		else Len[o]=Len[o<<1]+Len[o<<1|1];
	}
}Seg;
inline int H(int k){
	return lower_bound(qy+1,qy+m+1,k)-qy;
}
int main(){
 	freopen("t1.in","r",stdin);
	scanf("%d",&n);//H.clear();
	for(int i=1;i<=n;i++){
		int x0,x1,y0,y1;
		scanf("%d%d",&x0,&y1);
		scanf("%d%d",&x1,&y0);//注意数据的输入
		a[i]=(node){x0,y0,y1,1};
		a[i+n]=(node){x1,y0,y1,-1};
		qy[++m]=y0;qy[++m]=y1;
	}
	sort(qy+1,qy+m+1);
	m=unique(qy+1,qy+m+1)-qy-1;//离散化
//	for(int i=1;i<=m;i++)
//		H[qy[i]]=i;
	Seg.init(1,1,m);//线段树的初始化
	sort(a+1,a+n*2+1,cmp);
	for(int i=1;i<n*2;i++){
		Seg.add(1,H(a[i].y0),H(a[i].y1)-1,a[i].c);
		ans+=(long long)Seg.Len[1]*(a[i+1].x-a[i].x);
	}
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(扫描线,离散化,线段树)