Picture POJ - 1177(线段树+扫描线入门)

Picture

POJ - 1177

A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter.

Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1.


The corresponding boundary is the whole set of line segments drawn in Figure 2.


The vertices of all rectangles have integer coordinates.

Input

Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate.

0 <= number of rectangles < 5000
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.

Output

Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.

Sample Input

7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

Sample Output

228

周长的计算更复杂些,看这张图:

周长可以分成两部分计算,横线和竖线,如图将所有彩色的横线加起来就是横向的所有长度了

然后可以采用竖直方向的扫描线将竖线的所有长度求出来

那么怎么计算横线的长度呢?

横线的长度 = 【现在这次总区间被覆盖的长度和上一次总区间被覆盖的长度之差的绝对值】

这样用自下而上和从左往右的两次扫描即可得到答案。

但是,这样的方法显得有些笨,有没有更高端的方法呢?

再看一张图:

看出什么了吗?这张图在上面那张图的基础上多了几条竖线。

我的意思是说,我们可以只做一次自下而上的扫描就把横线竖线都算出来!

竖线的算法和上面说的方法一样:【现在这次总区间被覆盖的长度和上一次总区间被覆盖的长度之差的绝对值】

竖线要怎么计算?

首先我们现在改一下线段树保存的属性,我们用如下信息记录线段树的节点:

1. l , r : 该节点代表的线段的左右端点坐标

2.len : 这个区间被覆盖的长度(即计算时的有效长度)

3.s : 表示这个区间被覆盖了几次

4. lc , rc : 标记这个节点的左右两个端点是否被覆盖(0表示没有,1表示有)

5.num :这个区间有多少条不连续线段(这个区间被多少条线段覆盖)

这里的num涉及到竖线的计算,故解释一下,举几个例子:

   若区间[0,10]被[1,2][4,5]覆盖,则num = 2

   若区间[0,10]被[1,3][4,5]覆盖,则num = 1(两区间刚好连在一起)

   若区间[0,10]被[1,5][2,6]覆盖,则num = 1(两区间连起来还是一段)

然后就可以计算竖线了:

竖线的长度 = 【下一条即将被扫到的横线的高度 - 现在扫到的横线的高度】*2*num

乘2是因为每条线段有两个端点;

看上图中棕色线段的竖线有4条,因为棕色的横线由2条线段组成

白色线段的竖线只有2条,因为白色的横线由1条线段组成(虽然这1条线段是由许多线段组合而成,但它依旧只算1条线段)

这样,依旧只扫一次就可以算出周长。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define ls i<<1
#define rs i<<1|1
#define m(i) ((q[i].l+q[i].r)>>1)
typedef long long ll;
const int N = 5007;
const int X = 20007;
const int inf = 1<<29;
struct Edge{
	int l,r;
	int h;
	int f;
	bool operator < (const Edge &a)const{
		return h < a.h;
	}
}e[N*2];
struct Node{
	int l,r;
	int len;
	int s;
	bool lc,rc;//表示这个节点代表的区间左右两个端点是否被覆盖
	int num;//这个区间有多少不连续到线段
}q[X*4];
void pushup(int i){
	if(q[i].s){
		q[i].len = q[i].r - q[i].l + 1;
		q[i].lc = q[i].rc = 1;
		q[i].num = 1;
	}
	else if(q[i].l == q[i].r){
		q[i].len = 0;
		q[i].lc = q[i].rc = 0;
		q[i].num = 0;
	}
	else{
		q[i].len = q[ls].len + q[rs].len;
		q[i].lc = q[ls].lc;
		q[i].rc = q[rs].rc;
		q[i].num = q[ls].num + q[rs].num - (q[ls].rc & q[rs].lc);
	}
}
void build(int i,int l,int r){
	q[i].l = l;
	q[i].r = r;
	q[i].s = q[i].len = 0;
	q[i].lc = q[i].rc = q[i].num = 0;
	if(l == r) return;
	int mid = m(i);
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void update(int i,int l,int r,int xx){
	if(l == q[i].l && q[i].r == r){
		q[i].s += xx;
		pushup(i);
		return;
	}
	int mid = m(i);
	if(r <= mid) update(ls,l,r,xx);
	else if(l > mid) update(rs,l,r,xx);
	else{
		update(ls,l,mid,xx);
		update(rs,mid+1,r,xx);
	}
	pushup(i);
}
int main(){
	int n;
	while(scanf("%d",&n) != EOF){
		int x1,x2,y1,y2,mx = -inf,mn = inf;
		int tot = 0;
		for(int i = 0; i < n; i++){
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			mx = max(mx,max(x1,x2));
			mn = min(mn,min(x1,x2));
			Edge &t1 = e[tot];
			Edge &t2 = e[tot+1];
			t1.l = t2.l = x1;
			t1.r = t2.r = x2;
			t1.h = y1;
			t1.f = 1;
			t2.h = y2;
			t2.f = -1;
			tot += 2;
		}
		sort(e,e+tot);
		//数据小不用离散化
		int ans = 0;
		int last = 0;
		build(1,mn,mx-1);
		for(int i = 0; i < tot; i++){
			update(1,e[i].l,e[i].r-1,e[i].f);
			ans += abs(q[1].len - last);
			ans += (e[i+1].h - e[i].h) * 2 * q[1].num;
			last = q[1].len;
		}
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81079942