USACO(Picture)转载
转载:http://www.nocow.cn/index.php/Translate:USACO/picture#SAMPLE_INPUT
描述
N(N<5000) 张矩形的海报,照片和其他同样形状的图片贴在墙上。它们的边都是垂直的或水平的。每个矩形可以部分或者全部覆盖其他矩形。所有的矩形组成的集合的轮廓称为周长。写一个程序计算周长。
图 1 是一个有 7 个矩形的例子:
图 1.一个 7 个矩形的集合
对应的轮廓为图 2 所示的所有线段的集合:
图 2. 矩形集合的轮廓
所有矩形的顶点坐标均为整数。所有的坐标都在 [-10000,10000] 的范围内,并且任何一个矩形面积都为整数。结果的值可能需要 32 位有符号整数表示。
格式
PROGRAM NAME: picture
INPUT FORMAT:
(file picture.in)
INPUT FORMAT 第1行: N,张贴在墙上的矩形的数目。 第 2..N+1行 接下来的N行中,每行都有两个点的坐标,分别是矩形的左下角坐标和右上角坐标。每一个坐标由 X 坐标和 Y 坐标组成。
OUTPUT FORMAT:
(file picture.out) 只有一行,为一个非负整数,表示输入数据中所有矩形集合的轮廓长度。
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
算法一
离散化
把所有矩形离散化(就是将整个平面分成许多“竖条”或“横条”,对其操作),每个矩形都由四条边组成,分为纵边和横边。对纵边和横边分别扫描一次,以横边为例:
- 每个矩形的两条横边中,称下面的为始边,上面的为终边。
- 把每条横边以纵坐标从小到大排序,如果纵坐标相同,则应把始边排到终边之前。
- 依次枚举每条横边
- 如果当前边为始边,则把这条边的横向的所有点j的层数增加1,即为level[j]++。如果层数由0变为了1,则这一点一定是边缘点,总周长ans++。
- 如果当前边为终边,则把这条边的横向的所有点j的层数减少1,即为level[j]--。如果层数由1变为了0,则这一点一定是边缘点,总周长ans++。
同理按此方法扫描纵边,即可得到最后结果。
算法二
总思路:离散+线段树
首先是离散:
- 显然,我们有2n条纵线和2n条横线。算法中,我们只考虑纵线,因为横线的做法同纵线的做法是相同的。离散就是将这2n条线段按照从左到右的顺序排序(也就是按照每条纵线的横坐标从小到大排序),这里需要注意一点:如果出现两个相同横坐标的纵线段,属于所在矩形左边的线段 要排在
属于所在矩形右边的线段 的左边。(这两个线段属于不同的矩形)。开始没注意到。。结果错了。。
然后是线段树:
- 每个节点有6个属性:s,t,l,r,c,m,分别表示左边界、右边界、左子树、右子树、覆盖数、区间内
线段总长度。
- 对于2n条纵线段,属于矩形左边的线段添加该线段到线段树(覆盖数+1),属于矩形右边的线段则从线
段树中删除该线段(覆盖数-1)。做添加、删除线段的同时,要维护m属性。规则如下:
-
- 如果该段线段覆盖数(c)>0,则M即为线段长,
- 如果覆盖数(c)=0,则M为 左儿子的M+右儿子的M。(如果本身是叶子就为0)
- 每次操作线段后改变总长度(也可能不变),如果原来的长度为now,新的长度为new,如果new>now,则new-now算入答案ans。
源程序:
/*
ID: cmykrgb1
PROG: picture
LANG: C++
*/
//Written By CmYkRgB123
#include <iostream>
#include <fstream>
#include <cstdlib>
#define MAX 10001
using namespace std;
typedef struct
{
int s,t,p;
bool start;
}Line;
ifstream fi("picture.in");
ofstream fo("picture.out");
int N,ans=0;
int *level;
Line Lx[MAX],Ly[MAX];
inline int cmp(const void *a,const void *b)
{
if (((Line*)a)->p==((Line*)b)->p)
{
if (((Line*)a)->start)
return -1;
else
return 1;
}
return ((Line *)a)->p < ((Line *)b)->p ? -1 : 1;
}
void init()
{
int i,x1,x2,y1,y2;
fi >> N;
for (i=1;i<=N;i++)
{
fi >> x1 >> y2 >> x2 >> y1;
Lx[i*2-1].p=y1;
Lx[i*2-1].s=x1;
Lx[i*2-1].t=x2;
Lx[i*2-1].start=false;
Lx[i*2].p=y2;
Lx[i*2].s=x1;
Lx[i*2].t=x2;
Lx[i*2].start=true;
Ly[i*2-1].p=x1;
Ly[i*2-1].s=y2;
Ly[i*2-1].t=y1;
Ly[i*2-1].start=true;
Ly[i*2].p=x2;
Ly[i*2].s=y2;
Ly[i*2].t=y1;
Ly[i*2].start=false;
}
N*=2;
qsort(Lx+1,N,sizeof(Lx[0]),cmp);
qsort(Ly+1,N,sizeof(Ly[0]),cmp);
level=(int *)malloc(sizeof(int)*20002);
level+=10000;
}
void Scan(Line *L)
{
int i,j;
for (i=-10000;i<=10000;i++)
level[i]=0;
for (i=1;i<=N;i++)
{
if (L[i].start)
{
for (j=L[i].s;j<L[i].t;j++)
{
level[j]++;
if (level[j]==1)
ans++;
}
}
else
{
for (j=L[i].s;j<L[i].t;j++)
{
level[j]--;
if (level[j]==0)
ans++;
}
}
}
}
void print()
{
fo << ans << endl;
fi.close();
fo.close();
}
int main()
{
init();
Scan(Lx);
Scan(Ly);
print();
return 0;
}