[Java] POJ - 1177 线段树 + 扫描线(周长并)

一、内容

Picture

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

二、思路

  • POJ1151 一样都是扫描线的经典题目

  • 现在要求的是所有的周长的并集,我们还是考虑从下往上进行扫描,那么就分成了横线的周长竖线的周长

  • 横线的周长 = |每次的线段树维护的区间长度 - 上一次线段树维护的区间长度| 的绝对值

  • 竖线的周长 = 2条扫描线的高度 * 横线条数 * 2
    [Java] POJ - 1177 线段树 + 扫描线(周长并)_第1张图片

  • 如何计算横线条数:我们用3个数组cnt(保存这个区间的横线条数),lCover(这个区间的左区间是否被覆盖),rCover同理。

  • 在pushup() 中更新每个区间的横线条数。

三、代码

import java.util.Arrays;
import java.util.Scanner;

public class POJ_1177_周长并 {
	static int N = 10005;
	static int n, x1, x2, y1 ,y2;
	static boolean[] lCover = new boolean[N << 2];
	static boolean[] rCover = new boolean[N << 2];
	static int[] sum = new int[N << 2];
	static int[] len = new int[N << 2];
	static int[] cnt = new int[N << 2];//保存线段的条数
	static int[] X = new int[N << 1]; 
 	static Line1[] line = new Line1[N << 1];
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		int tem = 1;
		for (int i = 1; i <= n; i++) {
			x1 = sc.nextInt();
			y1 = sc.nextInt();
			x2 = sc.nextInt();
			y2 = sc.nextInt();
			X[tem] = x1;
			line[tem++] = new Line1(x1, x2, y1, 1);
			X[tem] = x2;
			line[tem++] = new Line1(x1, x2, y2, -1);
		}
		n <<= 1;
		Arrays.sort(X, 1, n | 1);
		Arrays.sort(line, 1, n | 1);
		X[0] = -99999;
		tem = 0;
		//去重  离散化
		for (int i = 1; i <= n; i++) {
			if (X[i] != X[tem]) {
				X[++tem] = X[i];
			}
		}
		long ans = 0;
		long last = 0;
		line[n + 1] = new Line1(0, 0, 0, 0);
		for (int i = 1; i <=n ; i++) {
			int xx = low_bound(1, tem, line[i].l);
			int yy = low_bound(1, tem, line[i].r);
			//yy - 1 是因为线段树维护的是区间 yy是当前点的下标,同理tem-1
			update(1, 1, tem - 1, xx, yy - 1, line[i].w);
			ans += abs(len[1] - last);
			ans += cnt[1] * 2 * (line[i + 1].h - line[i].h);
			last = len[1];
		}
		System.out.println(ans);
	}
	static long abs(long a) {
		if (a > 0) {
			return a;
		} else {
			return -a;
		}
	}
	static int low_bound(int l, int r, int v) {
		while (l < r) {
			int mid = (l + r) >> 1;
			if (X[mid] >= v) {
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		return l;
	}
	// 这个函数最关键  其他都是套模板
	static void pushup(int id, int l, int r) {
		int lId = id << 1;
		int rId = id << 1 | 1;
		if (sum[id] > 0) {
			len[id] = X[r + 1] - X[l];
			lCover[id] = rCover[id] = true;
			cnt[id] = 1;
		} else if (l == r) {
			len[id] = 0; 
			cnt[id] = 0;
			lCover[id] = rCover[id] = false;
		} else { 
			len[id] = len[lId] + len[rId]; 
			lCover[id] = lCover[lId];
			rCover[id] = rCover[rId];
			//减去1 是因为可能会凑成一条线段 , 凑成一条线段有2种可能 一种是左右区间全部覆盖,一种是左区间的右子区间覆盖和右区间的左子区间覆盖, 2种情况都可以写成下面一种。
			cnt[id] = cnt[lId] + cnt[rId] - (lCover[rId] && rCover[lId] ? 1 : 0);
		}
	}
	
	static void update(int id, int l, int r, int x, int y, int v) {
		if (x <= l && r <= y) {
			sum[id] += v;
			pushup(id, l, r);
			return;
		}
		int mid = (l + r) >> 1;
		if (x <= mid) {
			update(id << 1, l, mid, x, y, v);
		}
		if (y > mid) {
			update(id << 1 | 1, mid + 1, r, x, y, v);
		}
		pushup(id, l, r);
	} 
	
}
class Line1 implements Comparable<Line1>{
	int l, r, h, w;
	public Line1(int l, int r, int h, int w) {
		this.l = l;
		this.r = r;
		this.h = h;
		this.w = w;
	}
	public int compareTo(Line1 o) {
		return h - o.h;
	}
}

你可能感兴趣的:(算法)