首先将纵向的边当作一个个的event。每个Event都记录下这条边的两个端点的纵坐标与这条边的横坐标。然后以x坐标的大小对这些event进行排序。矩形的左纵向边是插入事件,右边是删除事件。
线段树可以在许多数据结构的书上有讲解,这里就不再重复。这里需要两个运算,一个是“测度”,求出线段树上,所有合并以后的长度。一个是求“线段的段数”,就是这个线段树上有几条线段。
算法:
建树
Event排序
读取每个Event
如果是插入事件,就插入线段树。
否则,就从线段树里删除。
求出根的"测度",和段数。
计算横向边的长度:上次的连续段数*2*(两次事件的x坐标之差)
计算纵向边的长度:上次的“测度”与这次“测度”的差的绝对值。
只需将所有的长度和加起来即可。
第2个分析:
这道题基本的方法是扫描。
首先计算所有的横边的长度,把矩形中所有横边离散出来,即用所有的纵边把横边切开,然后从左到右扫描每两段相邻纵边之间的部分。
对于每一部分,从上到下统计所有横边,把其中属于外边(即属于矩形的周长)的找出来,设有k条外边,而相邻纵边的距离是s,则此部分的横边长度为ks,所有横边的长度就是所有部分横边长度的和。
至于如何判断一条边是否属于外边,只要从上到下扫描,设一个累加值tot,遇到一个矩形的上边就+1,遇到一个矩形的下边就-1,则一条边属于外边当且仅当扫描到这条边时tot为0或扫描完这条边后tot为0。
再用类似的方法计算出纵边的长度。加和即为所求周长。
这种算法的复杂度是O(n2)的。还是有些慢。
改变数据结构,用线段树来优化。
如图,仍然是从左到右扫描所有的相邻纵边之间的部分,对于每一部分:
把其中的矩形的上下边看成区间(L和L’),则我们把这个区间插入线段树中,统计线段树中的独立连续区间(即任两个区间没有相交或相邻部分)数,设为h。
不难看出外边的个数就是2h,剩余的问题就和第一种方法一样了。
从左到右扫描每条纵边,如果这条边是矩形的左边,则把它作为区间插入线段树中,如果是右边就从线段树中把这个区间删除。在插入/删除区间的同时求出独立连续区间数,则它就是我们要求的h。
具体实现时,对于树中的每个结点,记录:
a,b-区间范围
cov-区间被覆盖的次数(完全覆盖)
line-区间内的独立连续区间数
为了求line,我们设la,lb,记录区间的左边右边是否被覆盖。
当前结点是叶结点(b-a=1),且cov>0,则la=lb=true,line=1
当前结点是叶结点,cov=0,则la=lb=false,line=0
当前结点是内结点,cov>0,则la=lb=true,line=1
当前结点是内结点,cov=0,则la=左儿子的la,lb=右儿子的lb
左儿子的lb和右儿子的la都为true,则line=左儿子的line+右儿子的line-1
否则line=左儿子的line+右儿子的line
这样,每次插入区间的复杂度是O(logm)的,m是坐标范围(-10000..10000),总共要插入2n条边,则总的复杂度是O(nlogm)的。
第3个 PASCAL的实现:
关于这题的线段树中需要在一棵树中记录以下元素
s,t线段的范围
c矩形的覆盖情况,其中线段预先按垂直方向坐标排序,这样每次覆盖可以从一个方向推进层次.
v在这个范围中边缘的长度
l,r左右子树
然后就进行线段添加
procedure add(v0:longint);
begin
1.若c>0(区间没被覆盖)则将线段树这个区间加到v
2.若区间包含在欲添加的线段则将这个线段的层次关键字加到c中,若c>0则将线段树这个区间加到v
3.对左右子区间add(left);add(right)
4.将左右子区间递归结果返回v0
end;
初始时add(1),然后1所以记录的v就是这条线段所多出来的边缘
{
TASK:picture
LANG:PASCAL
}
{$inline on}
program picture;
const
maxn=5000 shl 1+1;
mx=maxint;
type
line=record
s,t,f:integer;
r:shortint;
end;
recttree=record
s,t,c:integer;
left,right:word;
v:longint;
end;
arrline=array[1..maxn] of line;
var
n,minx,maxx,miny,maxy,total,ll,rr,d:longint;
row,col:arrline;
tree:array[1..maxn shl 2] of recttree;
procedure assignfiles;inline;
begin
assign(input,'picture.in');reset(input);
assign(output,'picture.out');rewrite(output);
end;
procedure closefiles;inline;
begin
close(input);
close(output);
end;
procedure quicksort(f,l:integer;var arr:arrline);inline;
procedure swap(var x,y:line);inline;
var
temp:line;
begin
temp:=x;
x:=y;
y:=temp;
end;
procedure qs(f,l:integer);
var
i,j:integer;
x:line;
begin
i:=f;
j:=l;
x:=arr[(i+j)shr 1];
while i<=j do
begin
while (arr[i].f<x.f)or((arr[i].f=x.f)and(arr[i].r>x.r)) do inc(i);
while (arr[j].f>x.f)or((arr[j].f=x.f)and(arr[j].r<x.r)) do dec(j);
if i<=j then
begin
swap(arr[i],arr[j]);
inc(i);
dec(j);
end;
end;
if i<l then qs(i,l);
if j>f then qs(f,j);
end;
begin
qs(f,l);
end;
procedure build(ll,rr:integer);inline;
var
mid:integer;
root:word;
begin
root:=total;
with tree[root] do begin s:=ll;t:=rr;c:=0;left:=0;right:=0;v:=0;end;
mid:=(ll+rr) div 2;
if rr-ll>1 then
begin
inc(total);
tree[root].left:=total;
build(ll,mid);
inc(total);
tree[root].right:=total;
build(mid,rr);
end;
end;
procedure add(now:word);inline;
var
mid:integer;
begin
with tree[now] do
if c>0 then v:=t-s else v:=0;
if (ll<=tree[now].s)and(rr>=tree[now].t) then
begin
inc(tree[now].c,d);
with tree[now] do
if c>0 then v:=t-s else v:=0;
end
else begin
mid:=(tree[now].s+tree[now].t)div 2;
if (ll<mid)and(tree[now].left<>0)then add(tree[now].left);
if (rr>mid)and(tree[now].right<>0)then add(tree[now].right);
end;
with tree[now] do
if v=0 then
begin
if left<>0 then inc(v,tree[left].v);
if right<>0 then inc(v,tree[right].v);
end;
end;
procedure init;inline;
var
i,p,lbx,lby,rtx,rty:integer;
begin
readln(n);
minx:=mx;
miny:=mx;
maxx:=-mx;
maxy:=-mx;
p:=0;
for i:=1 to n do
begin
readln(lbx,lby,rtx,rty);
if lbx<minx then minx:=lbx;
if rtx>maxx then maxx:=rtx;
if lby<miny then miny:=lby;
if rty>maxy then maxy:=rty;
inc(p);
with row[p] do begin s:=lby;t:=rty;f:=lbx;r:=1;end;
with col[p] do begin s:=lbx;t:=rtx;f:=lby;r:=1;end;
inc(p);
with row[p] do begin s:=lby;t:=rty;f:=rtx;r:=-1;end;
with col[p] do begin s:=lbx;t:=rtx;f:=rty;r:=-1;end;
end;
end;
procedure work;inline;
var
i:integer;
ans,old,now:longint;
begin
quicksort(1,n shl 1,row);
total:=1;
build(miny,maxy);
ll:=row[1].s;
rr:=row[1].t;
d:=1;
add(1);
ans:=tree[1].v;
old:=ans;
for i:=2 to n shl 1 do
begin
with row[i] do begin ll:=s;rr:=t;d:=r;end;
add(1);
now:=tree[1].v;
if now>old then inc(ans,now-old);
old:=now;
end;
quicksort(1,n shl 1,col);
total:=1;
build(minx,maxx);
ll:=col[1].s;
rr:=col[1].t;
d:=1;
add(1);
old:=tree[1].v;
inc(ans,old);
for i:=2 to n shl 1 do
begin
with col[i] do begin ll:=s;rr:=t;d:=r;end;
add(1);
now:=tree[1].v;
if now>old then inc(ans,now-old);
old:=now;
end;
writeln(ans shl 1);
end;
begin
assignfiles;
init;
work;
closefiles;
end.