USACO 5.5 PICTURE 题解

首先将纵向的边当作一个个的event。每个Event都记录下这条边的两个端点的纵坐标与这条边的横坐标。然后以x坐标的大小对这些event进行排序。矩形的左纵向边是插入事件,右边是删除事件。

  线段树可以在许多数据结构的书上有讲解,这里就不再重复。这里需要两个运算,一个是“测度”,求出线段树上,所有合并以后的长度。一个是求“线段的段数”,就是这个线段树上有几条线段。

  算法:

    建树

   Event排序

  读取每个Event

  如果是插入事件,就插入线段树。

  否则,就从线段树里删除。

  求出根的"测度",和段数。

  计算横向边的长度:上次的连续段数*2*(两次事件的x坐标之差)

  计算纵向边的长度:上次的“测度”与这次“测度”的差的绝对值。

 只需将所有的长度和加起来即可。

 

第2个分析:

 

这道题基本的方法是扫描。

首先计算所有的横边的长度,把矩形中所有横边离散出来,即用所有的纵边把横边切开,然后从左到右扫描每两段相邻纵边之间的部分。

对于每一部分,从上到下统计所有横边,把其中属于外边(即属于矩形的周长)的找出来,设有k条外边,而相邻纵边的距离是s,则此部分的横边长度为ks,所有横边的长度就是所有部分横边长度的和。

至于如何判断一条边是否属于外边,只要从上到下扫描,设一个累加值tot,遇到一个矩形的上边就+1,遇到一个矩形的下边就-1,则一条边属于外边当且仅当扫描到这条边时tot0或扫描完这条边后tot0

再用类似的方法计算出纵边的长度。加和即为所求周长。

这种算法的复杂度是O(n2)的。还是有些慢。

 

改变数据结构,用线段树来优化。

查看更多精彩图片

 

如图,仍然是从左到右扫描所有的相邻纵边之间的部分,对于每一部分:

把其中的矩形的上下边看成区间(LL’),则我们把这个区间插入线段树中,统计线段树中的独立连续区间(即任两个区间没有相交或相邻部分)数,设为h

不难看出外边的个数就是2h,剩余的问题就和第一种方法一样了。

从左到右扫描每条纵边,如果这条边是矩形的左边,则把它作为区间插入线段树中,如果是右边就从线段树中把这个区间删除。在插入/删除区间的同时求出独立连续区间数,则它就是我们要求的h

 

具体实现时,对于树中的每个结点,记录:

ab-区间范围

cov-区间被覆盖的次数(完全覆盖)

line-区间内的独立连续区间数

 

为了求line,我们设lalb,记录区间的左边右边是否被覆盖。

当前结点是叶结点(b-a=1),且cov>0,则la=lb=trueline=1

当前结点是叶结点,cov=0,则la=lb=falseline=0

当前结点是内结点,cov>0,则la=lb=trueline=1

当前结点是内结点,cov=0,则la=左儿子的lalb=右儿子的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.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值