Picture
Description
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 Source |
(x轴)扫描线:沿x轴从左到右的一条条垂直于x轴的线段(一个矩形两条扫描线)每个扫描线存储四个信息:
扫描线所在x坐标、扫描线上端和下端y坐标、扫描线是入边还是出边(在同一个矩形的左边还是右边)
解题方法:用线段树做,线段树存储的是y轴的覆盖情况,先将y轴出现的坐标离散化,若离散化后得出k个不同的
点,线段树就有k-1个叶子,每个叶子表示相邻两点之间的那段距离。每个节点存储5个信息:当前节点被覆盖的长
度、当前节点至少被完全覆盖多少次(这个没必要下放)、当前节点的最左端和最右段分别是否被覆盖和当前节点
有多少个不连续的覆盖区域。初始化所有节点所有变量为0,然后从左到右遍历每个扫描线,依次更新并求出相邻
两条扫描线之间的周长(有些题会是求面积),周长包括竖边和横边,其中竖边长为|更新后线段树总覆盖区域-
更新前线段树总覆盖区域|,横边总长为更新前不连续覆盖区域个数×(相邻扫描线的x坐标差)
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef struct /*扫描线*/
{
int x;
int h, d;
int flag;
}Square;
typedef struct /*线段树节点*/
{
int line;
int lc, rc;
int cnt;
int s1;
}Point;
Point tre[88888], temp;
Square s[11015];
int pos[11015];
void Update(int l, int r, int x, int a, int b, int t); /*更新*/
void Atonce(int l, int r, int x);
int Bsech(int l, int r, int k) /*通过二分搜索找到坐标对应离散化后的点*/
{
int m;
m = (l+r)/2;
if(pos[m]<k)
return Bsech(m+1, r, k);
if(pos[m]>k)
return Bsech(l, m-1, k);
return m;
}
bool comp(Square a, Square b)
{
if(a.x<b.x || a.x==b.x && a.flag==1 && b.flag==-1) /*注意:如果某个入边和某个出边的x坐标相等,入边一定要排在那个出边前面*/
return 1;
return 0;
}
int main(void)
{
int i, n, x1, y1, x2, y2, len, sum;
while(scanf("%d", &n)!=EOF)
{
len = 1;
sum = 0;
memset(tre, 0, sizeof(tre));
for(i=1;i<=n;i++)
{
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
s[i*2-1].x = x1, s[i*2].x = x2;
s[i*2-1].h = s[i*2].h = y2;
s[i*2-1].d = s[i*2].d = y1;
s[i*2-1].flag = 1, s[i*2].flag = -1;
pos[i*2-1] = y1, pos[i*2] = y2;
}
n = n*2;
sort(s+1, s+n+1, comp);
sort(pos+1, pos+n+1);
for(i=2;i<=n;i++)
{
if(pos[i]!=pos[i-1])
pos[++len] = pos[i];
}
for(i=1;i<=n;i++)
{
x1 = Bsech(1, len, s[i].d);
x2 = Bsech(1, len, s[i].h);
temp = tre[1];
Update(1, len-1, 1, x1, x2-1, s[i].flag); /*更新区间x1和x2-1, s[i].flag表示应该添加还是删除该段*/
sum += abs(tre[1].s1-temp.s1)+temp.line*2*(s[i].x-s[i-1].x);
}
printf("%d\n", sum);
}
return 0;
}
void Update(int l, int r, int x, int a, int b, int t)
{
int m;
if(l>=a && r<=b)
{
tre[x].cnt += t;
Atonce(l, r, x);
return;
}
m = (l+r)/2;
if(a<=m)
Update(l, m, x*2, a, b, t);
if(b>=m+1)
Update(m+1, r, x*2+1, a, b, t);
Atonce(l, r, x);
}
void Atonce(int l, int r, int x)
{
if(tre[x].cnt>=1) /*如果当前节点至少被完全覆盖1次或以上*/
tre[x].s1 = pos[r+1]-pos[l], tre[x].line = 1, tre[x].lc = tre[x].rc = 1;
else
tre[x].s1 = tre[x*2].s1+tre[x*2+1].s1, tre[x].line = 0, tre[x].lc = tre[x].rc = 0;
if(tre[x].cnt==0 && l!=r)
{
if(tre[x*2].lc==1)
tre[x].lc = 1;
if(tre[x*2+1].rc==1)
tre[x].rc = 1;
tre[x].line = tre[x*2].line+tre[x*2+1].line; /*当前节点不连续覆盖区域的个数等于两个儿子不连续覆盖区域的个数之和*/
if(tre[x*2].rc==1 && tre[x*2+1].lc==1) /*当然如果左儿子的右端和右儿子的左端连续,那么当前节点不连续覆盖区域的个数就要-1*/
tre[x].line--;
}
}