题目传送门:https://www.luogu.org/problemnew/show/P1856
墙上贴着许多形状相同的海报、照片。它们的边都是水平和垂直的。每个矩形图片可能部分或全部的覆盖了其他图片。所有矩形合并后的边长称为周长。
编写一个程序计算周长。
如图1所示7个矩形。
如图2所示,所有矩形的边界。所有矩形顶点的坐标都是整数。
输入文件的第一行是一个整数N(0<=N<5000),表示有多少个矩形。接下来N行给出了每一个矩形左下角坐标和右上角坐标(所有坐标的数值范围都在-10000到10000之间)。
输出文件只有一个正整数,表示所有矩形的周长。
本蒟蒻第一眼看到这道题,跟线段树有什么关系???
好吧,其实是可以暴力做出来的,但是一点也不优秀!!!
所以我们来想一下线段树的做法
预处理:把每一个矩形的两个长和两个宽记录在数组里,并且要记录每一条边的类型。
每个边的类型有两种,矩形的左边与下边记为类型一,矩形的右边和上边记为类型二.
把每个垂直于X轴的边按横坐标从左往右排序,每一个平行于X轴的边按纵坐标从下往上排序。
但是,坑点来了!!当两个边的位置相同时,要把一类边放在前面!(之后讲原因)
将两个坐标轴分别看成两个线段树,Y轴向右平移,X轴向上平移(负数坐标只需要整体+10000)
在线段树里维护一个cover值,其中cover[i]表示从i.l到i.r这个区间被完全覆盖的次数
当扫描到一条线段时,有两种情况:
1.这是一个一类线段,那么就将这条线段对应的区间的cover值+1.
这个线段对总周长做出的贡献就是插入前被覆盖区间的总长A1减去插入后被覆盖区间的总长B1的绝对值。
2.这是一个二类线段,那么就将这条线段对应的区间的cover值-1.
同理,这个线段对总周长做出的贡献就是插入前被覆盖区间的总长A1减去插入后被覆盖区间的总长B1的绝对值。
如何算出当前被覆盖区间的总长呢?可以对整个线段树做一次查询,对于一个cover值不为0的区间,说明他已经被完全覆盖了,所以被覆盖区间的总长就加上(l-r),找到一个完全覆盖的区间之后便不往这个区间的下层查询了,因为很明显这样会算重。
但是为什么要把一类边放在前面呢?考虑这样一种情况:
一个一类边和一个二类边重合了。这个时候,我们不能先算二类边,因为在处理下一条一类边的时候会再重复加一次!
贴上代码:(本蒟蒻用了动态开点)
#include
#include
#include
#include
#include
#define maxn 80005
#define maxm 5005
using namespace std;
struct line
{
int x,y,pos,type;
}ver[maxm*2],hor[maxm*2];
int n,cnt,ans,tot=1;
int ls[maxn],rs[maxn],cover[maxn];
int getsum(int p,int l,int r)
{
if(cover[p]!=0) return (r-l);
if((r-l==1)||((!ls[p])&&(!rs[p]))) return 0;
int mid=(l+r)>>1,ret=0;
if(!ls[p]) ls[p]=++tot;
if(!rs[p]) rs[p]=++tot;
ret+=getsum(ls[p],l,mid);
ret+=getsum(rs[p],mid,r);
return ret;
}
void modify(int p,int l,int r,int x,int y,int val)
{
if(x<=l&&r<=y)
{
cover[p]+=val;
return;
}
int mid=(l+r)>>1;
if(xy>=l)
{
if(!ls[p]) ls[p]=++tot;
modify(ls[p],l,mid,x,y,val);
}
if(y>mid&&x<=r)
{
if(!rs[p]) rs[p]=++tot;
modify(rs[p],mid,r,x,y,val);
}
}
bool cmp(line a,line b)
{
if(a.pos==b.pos) return a.typereturn a.pospos;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x,y,x1,y1;
scanf("%d%d%d%d",&x,&y,&x1,&y1);
x+=10001,y+=10001,x1+=10001,y1+=10001;
ver[++cnt].x=y1;
ver[cnt].y=y;
ver[cnt].pos=x;
ver[cnt].type=1;
//right line init
hor[cnt].x=x1;
hor[cnt].y=x;
hor[cnt].pos=y;
hor[cnt].type=1;
//lower line init
ver[++cnt].x=y1;
ver[cnt].y=y;
ver[cnt].pos=x1;
ver[cnt].type=2;
//left line init
hor[cnt].x=x1;
hor[cnt].y=x;
hor[cnt].pos=y1;
hor[cnt].type=2;
//upper line init
}
sort(hor+1,hor+2*n+1,cmp);
sort(ver+1,ver+2*n+1,cmp);
for(int i=1;i<=2*n;i++)
{
if(ver[i].type==1)
{
int s1=getsum(1,1,maxn),s2;
modify(1,1,maxn,ver[i].y,ver[i].x,1);
s2=getsum(1,1,maxn);
ans+=abs(s1-s2);
}
else
{
int s1=getsum(1,1,maxn),s2;
modify(1,1,maxn,ver[i].y,ver[i].x,-1);
s2=getsum(1,1,maxn);
ans+=abs(s1-s2);
}
}
memset(cover,0,sizeof(cover));
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
tot=1;
for(int i=1;i<=2*n;i++)
{
if(hor[i].type==1)
{
int s1=getsum(1,1,maxn),s2;
modify(1,1,maxn,hor[i].y,hor[i].x,1);
s2=getsum(1,1,maxn);
ans+=abs(s1-s2);
}
else
{
int s1=getsum(1,1,maxn),s2;
modify(1,1,maxn,hor[i].y,hor[i].x,-1);
s2=getsum(1,1,maxn);
ans+=abs(s1-s2);
}
}
printf("%d",ans);
}