线段树+线扫描+离散化
1. 题意:给出多个矩形坐标,求所有矩形的组合体的周长
2. 这道题的突破口是线扫描,假想一条垂直于x轴的线从左往右扫描,以矩形y轴方向的边为事件,这样只要知道当前发生事件时矩形的覆盖y轴的长度(为了计算y轴方向的边),以及由几块孤立的区域组成(为了计算x轴方向的边),但是问题就是如何在每次更新事件时更新这两个属性
3. 这就到了线段树出场,因为如果要维护这2个属性,必须要在矩形左边的边时加入它,在右边时把它删除,能快速做到对一个线段快速修改就是线段树了,而且对于维护一个区域内的线段的总长度,以及孤立的线段数量,它也不难实现,但这时候问题来了,坐标的范围是-10000~10000,正常用这个坐标来更新线段树的话速度可能不够。所以最好的办法就是先对矩形边的y坐标离散化(不过本题中如果数据部太变态,不离散化估计也能过)
4. 线段树的维护:这也是一个难点,越用这东西越觉得它有很多东西值得钻研。
本题中要维护的2个线段树属性
a) 覆盖的总线段长
因为对同个区域插入多次,他的总长度还是不变,对一个区域删除的次数要等于他插入的次数才能把区域中的长度减少,所以要在每个节点加上覆盖次数cover
b) 孤立线段的数量
因为要判断2个子区域是否相连,加上bl,br记录当前节点左右边界是否被覆盖
5. 注意点
a) 线段树的叶子节点区间长度为1,为0的话计算不了长度,所以在build时,要注意了
附代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 5010
#define M 20000
#define CUT 10000
#define INF 0x7f7f7f7f
struct Line
{
int x;
int ytop,ylow;
int yIndextop,yIndexlow;
bool bst;
}line[N<<1];
struct Tree
{
int Num,Sum,flag;
bool bl,br;
int st,en;
}g[N<<2];
int number[M];
int S[N*2];
int n,top,n2;
bool cmp(const Line&x,const Line&y)
{
if(x.x != y.x)
{
return x.x<y.x;
}
return x.bst;
}
void build(int pos,int st,int en)
{
g[pos].st=st;
g[pos].en=en;
g[pos].bl=g[pos].br=false;
g[pos].Num=g[pos].Sum=g[pos].flag=0;
if(st+1>=en)
{
return;
}
int mid=(st+en)>>1;
build(pos<<1,st,mid);
build(pos<<1|1,mid,en);
}
void Update(int pos)
{
if(g[pos].flag>0)
{
g[pos].Sum=S[g[pos].en]-S[g[pos].st];
g[pos].Num=1;
g[pos].bl=g[pos].br=true;
}
else
{
g[pos].Sum=g[pos<<1].Sum+g[pos<<1|1].Sum;
g[pos].Num=g[pos<<1].Num+g[pos<<1|1].Num;
if(g[pos<<1].br && g[pos<<1|1].bl)
{
g[pos].Num--;
}
g[pos].bl=g[pos<<1].bl;
g[pos].br=g[pos<<1|1].br;
}
}
void insert(int pos,int ll,int rr)
{
if(ll==g[pos].st && rr==g[pos].en)
{
g[pos].flag++;
}
else
{
int mid=(g[pos].st+g[pos].en)>>1;
if(rr<=mid)
{
insert(pos<<1,ll,rr);
}
else if(ll >= mid)
{
insert(pos<<1|1,ll,rr);
}
else
{
insert(pos<<1,ll,mid);
insert(pos<<1|1,mid,rr);
}
}
Update(pos);
}
void DeleteLine(int pos,int ll,int rr)
{
if(ll==g[pos].st && rr==g[pos].en)
{
g[pos].flag--;
}
else
{
int mid=(g[pos].st+g[pos].en)>>1;
if(rr<=mid)
{
DeleteLine(pos<<1,ll,rr);
}
else if(ll >= mid)
{
DeleteLine(pos<<1|1,ll,rr);
}
else
{
DeleteLine(pos<<1,ll,mid);
DeleteLine(pos<<1|1,mid,rr);
}
}
Update(pos);
}
int SweepLine()
{
int ans;
insert(1,line[0].yIndexlow,line[0].yIndextop);
ans=g[1].Sum;
int last=ans;
int nn2=n2-1;
for(int i=1;i<nn2;++i)
{
ans+=g[1].Num*2*(line[i].x-line[i-1].x);
if(line[i].bst)
{
insert(1,line[i].yIndexlow,line[i].yIndextop);
}
else
{
DeleteLine(1,line[i].yIndexlow,line[i].yIndextop);
}
ans+=abs(last-g[1].Sum);
last=g[1].Sum;
}
ans+=2*(line[n2-1].x-line[n2-2].x);
DeleteLine(1,line[n2-1].yIndexlow,line[n2-1].yIndextop);
ans+=abs(last-g[1].Sum);
return ans;
}
int main()
{
//freopen("input.txt","r",stdin);
scanf("%d",&n);
n2=n*2;
memset(number,-1,sizeof(number));
int X1,X2,Y1,Y2;
int biggest=0,lowest=INF;
for(int i=0;i<n;++i)
{
scanf("%d%d%d%d",&X1,&Y1,&X2,&Y2);
X1+=CUT;
X2+=CUT;
Y1+=CUT;
Y2+=CUT;
line[i].x=X1;
line[i].ylow=Y1;
line[i].ytop=Y2;
line[i].bst=true;
line[i+n].x=X2;
line[i+n].ylow=Y1;
line[i+n].ytop=Y2;
line[i+n].bst=false;
number[Y1]=1;
number[Y2]=1;
biggest=max(biggest,Y2);
lowest=min(lowest,Y1);
}
top=0;
for(int i=lowest;i<=biggest;++i)
{
if(number[i] != -1)
{
S[top++]=i;
}
}
sort(S,&S[top]);
for(int i=0;i<top;++i)
{
number[S[i]]=i;
}
for(int i=0;i<n2;++i)
{
line[i].yIndexlow=number[line[i].ylow];
line[i].yIndextop=number[line[i].ytop];
}
sort(line,&line[n2],cmp);
build(1,0,top-1);
printf("%d\n",SweepLine());
return 0;
}