Time Limit: 2000MS | Memory Limit: 10000K | |
Total Submissions: 10230 | Accepted: 5422 |
Description
Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1.
![](https://i-blog.csdnimg.cn/blog_migrate/20d57b877fcc7082d971e6e66ada3cfa.jpeg)
The corresponding boundary is the whole set of line segments drawn in Figure 2.
![](https://i-blog.csdnimg.cn/blog_migrate/9b7f8dfac2af36a4a557c087c5f55e0e.jpeg)
The vertices of all rectangles have integer coordinates.
Input
0 <= number of rectangles < 5000
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.
Output
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
首先说离散化:因为有的时候题目中给出的数据范围可能非常大,如果直接建立成线段树的话,绝对超内存,怎么办呢?其实这里所谓离散化就是把这些数排列在数组中,然后用数组的下标来代替这个数,这样我们最多只有N*2个点
如何求周长? 那么现在我假设读者都明白离散化,以及线段树了!
具体做法有两种形式 可以对x离散也可以对y 离散! 我这里以对y 进行离散(这样可能更符和我们平常的思维方式)
1:首先将矩形的竖边全部存起来(要用一个标记变量标记该边是否为入边),然后按照竖边的X 的大小排好序;
2:在存储矩形竖边的同时要把矩形的Y坐标存起来,然后排序,最后去掉重复的点!
3:建树根据第二步中最后留下来点的个数num建立一个区间长度为[0,num-1]的线段树;
4:这步很关键了,把排好序的竖边从左到右开始扫描了,如果是矩形的左边那么就插入,要是是矩形的右边了那么就从线段树里删除了!(在这里每次插入和删除线段树要维护好两个重要的值,一个是当前线段树的被覆盖的区间长度的总和,第二个是当前线段树中被覆盖的区间有多少个)!
PS:其中这两句代码非常重要,读者可以画个简单的图进行理解,起始的时候我没明白要记录线段段数的作用,仔细研究了这部分代码发现算线段的段数是为了求得横边的长度,还有一点要注意的是,这棵线段树要建成节点为单元线段的形式,即如果区间为[0,3]
线段树要建成 [0,3]
/ \
[0,1] [1,3]
/ \
[1,2] [2,3]
#include<stdio.h>
#include<algorithm>
#include<math.h>
#define N 10005
using namespace std;
int b[N];
struct node
{
int x,y;
int cover;//区间被覆盖的次数
int len;//区间内代表的长度
int sum;//区间中被覆盖的总长度
bool lcover,rcover;//标记左右端点是否被覆盖,用于合并区间时统计区间内的离散线段数
int segnum;//区间内被分成的段数
}a[N*3];
struct line
{
int st,ed;//竖边的两个y值
int x;//此条边的x值
int in;//是否为入边
}bian[N];
bool cmp(line a,line b)
{
return a.x<b.x;
}
void build(int t,int x,int y)
{
a[t].x=x;
a[t].y=y;
a[t].sum=0;
a[t].len=b[y]-b[x];
a[t].cover=0;
a[t].lcover=a[t].rcover=false;
a[t].segnum=0;
if(x+1==y) return;
int mid=(x+y)>>1,temp=t<<1;
build(temp,x,mid);
build(temp+1,mid,y);
}
void pushdown(int t,int x,int y)
{//求该区间所包含的线段数总量(就是含有不相交的线段的条数),以及区间包含线段的总长度。
int temp=t<<1;
if(a[t].cover)
{
a[t].sum=a[t].len;
a[t].lcover=a[t].rcover=true;
a[t].segnum=1;
}
else if(y-x>1)
{
a[t].lcover=a[temp].lcover;
a[t].rcover=a[temp+1].rcover;
a[t].sum=a[temp].sum+a[temp+1].sum;
a[t].segnum=a[temp].segnum+a[temp+1].segnum-a[temp].rcover*a[temp+1].lcover;
}
else
{
a[t].lcover=a[t].rcover=false;
a[t].sum=0;
a[t].segnum=0;
}
}
void update(int t,int x,int y,int flag)
{
if(a[t].x==x&&a[t].y==y)
{
if(flag==1)//插入一条线段
a[t].cover++;
else//删除一条线段
a[t].cover--;
pushdown(t,x,y);
return;
}
int mid=(a[t].x+a[t].y)>>1,temp=t<<1;
if(y<=mid)
update(temp,x,y,flag);
else if(x>=mid)
update(temp+1,x,y,flag);
else
{
update(temp,x,mid,flag);
update(temp+1,mid,y,flag);
}
pushdown(t,a[t].x,a[t].y);
}
int main()
{
int n,i,k,x1,y1,x2,y2,rlen,ans,x,y;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
bian[i*2].st=y1;bian[i*2].ed=y2; bian[i*2].x=x1;bian[i*2].in=1;//标记为入边
bian[i*2+1].st=y1;bian[i*2+1].ed=y2; bian[i*2+1].x=x2; bian[i*2+1].in=0;
b[i*2]=y1; b[i*2+1]=y2;
}
sort(b,b+n*2);
sort(bian,bian+n*2,cmp);
k=1;
for(i=1;i<n*2;i++)//去重
{
if(b[i]!=b[i-1])
b[k++]=b[i];
}
build(1,0,k-1);
rlen=0; ans=0;
for(i=0;i<n*2-1;i++)
{//lower_bound函数返回一个元素在容器中的迭代器,数组可以看成特殊的容器,所以这里返回的迭代器就是指针
x=lower_bound(b,b+k,bian[i].st)-b;
y=lower_bound(b,b+k,bian[i].ed)-b;
//printf("%d %d\n",x,y);
if(bian[i].in==1)
update(1,x,y,1);
else
update(1,x,y,0);
ans+=(bian[i+1].x-bian[i].x)*2*a[1].segnum;
ans+=abs(a[1].sum-rlen);
rlen=a[1].sum;
}
//特殊处理最后一条出边,因为没有下一条竖边了
x=lower_bound(b,b+k,bian[i].st)-b;
y=lower_bound(b,b+k,bian[i].ed)-b;
update(1,x,y,0);
ans+=abs(a[1].sum-rlen);
printf("%d\n",ans);
}
return 0;
}