Description
求n个矩形的周长并
Input
第一行为一整数n表示矩形个数,之后n行每行四个整数x1,y1,x2,y2表示该矩形左下端点与右上端点的横纵坐标
Output
输出这n个矩形的周长并
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
Solution
类似矩形面积并,首先用左右两边表示一个矩形并标记每条线段是左边还是右边,然后对这些线段按x升序排序,对y轴建树(对每个线段的两端点纵坐标排完序离散化之后建线段树),用一根扫描线从左往右扫这些线段,线段树记录的是当前区间中有多少段不连续的已经被扫描的区间,被完全覆盖的次数,左右端点是否被覆盖,被覆盖的长度,每次扫描线扫到左边就在线段树中覆盖这个区间,遇到右端点就取消这段区间的覆盖,维护这个区间中被连续覆盖部分的数量乘二倍的相邻扫描线间距即为这次扫描部分横边的长度,这个区间被覆盖的长度减去上一根扫描线被覆盖长度的绝对值即为这次扫描部分纵边的长度,具体如何维护见代码
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 11111
struct Tree
{
int left,right,num,flag,len;
bool lf,rf;
}T[4*maxn];
//num表示这个区间被连续覆盖部分的数量
//flag标记这个区间是否被完全覆盖
//len表示这个区间被覆盖的长度
//lf和rf表示这个区间的左右端点是否被覆盖
struct Line
{
int x,y1,y2;
bool flag;
}line[maxn];
int n,cnt,res,y[maxn],ans,num,len;
void add(int x,int y1,int y2,int yy,int flag)
{
line[cnt].x=x;line[cnt].y1=y1;line[cnt].y2=y2;line[cnt].flag=flag;y[cnt++]=yy;
}
bool cmp(Line l1,Line l2)
{
if(l1.x!=l2.x) return l1.x<l2.x;
return l1.flag>l2.flag;
}
void build(int l,int r,int t)
{
T[t].left=l;
T[t].right=r;
T[t].num=T[t].len=T[t].flag=T[t].lf=T[t].rf=0;
if(l+1==r)return ;
int mid=(l+r)>>1;
build(l,mid,2*t);
build(mid,r,2*t+1);
}
void update_num(int t)
{
if(T[t].flag) T[t].lf=T[t].rf=T[t].num=1;
else if(T[t].left+1==T[t].right) T[t].lf=T[t].rf=T[t].num=0;
else
{
T[t].lf=T[2*t].lf;
T[t].rf=T[2*t+1].rf;
T[t].num=T[2*t].num+T[2*t+1].num-T[2*t].rf*T[2*t+1].lf;
}
}
void update_len(int t)
{
if(T[t].flag) T[t].len=y[T[t].right]-y[T[t].left];
else if(T[t].left+1==T[t].right) T[t].len=0;
else T[t].len=T[2*t].len+T[2*t+1].len;
}
void update(int l,int r,int z,int t)
{
if(y[T[t].left]==l&&y[T[t].right]==r) T[t].flag+=z;
else if(T[t].left+1==T[t].right) return ;
else
{
int mid=(T[t].left+T[t].right)>>1;
if(r<=y[mid]) update(l,r,z,2*t);
else if(l>=y[mid]) update(l,r,z,2*t+1);
else
{
update(l,y[mid],z,2*t);
update(y[mid],r,z,2*t+1);
}
}
update_num(t);
update_len(t);
}
int main()
{
while(~scanf("%d",&n))
{
cnt=0;ans=0;len=num=0;
int x1,x2,y1,y2;
while(n--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
//用左右两边表示该矩形
add(x1,y1,y2,y1,1);
add(x2,y1,y2,y2,0);
}
sort(line,line+cnt,cmp);//对这些线段按横坐标升序排序
//对y轴建树
sort(y,y+cnt);
res=unique(y,y+cnt)-y;
build(0,res-1,1);
for(int i=0;i<cnt;i++)
{
if(line[i].flag) update(line[i].y1,line[i].y2,1,1);//扫到左边就在线段树中覆盖这个区间
else update(line[i].y1,line[i].y2,-1,1);//扫到右边就在线段树中取消这个区间的覆盖
if(i) ans+=2*num*(line[i].x-line[i-1].x);//横边长度
ans+=abs(T[1].len-len);//纵边长度
len=T[1].len;//更新被覆盖的长度
num=T[1].num;//更新被连续覆盖部分的数量
}
printf("%d\n",ans);
}
return 0;
}