POJ 1988 [Cube Stacking]

题目的大意是给你一些相同的立方体,共有1..N这些位置,开始每个位置上只含一个立方体,每个立方体标号1..N,然后让你执行p个操作,但只有两种操作:Move(移动)和 Count(计数)。

1)Move,输入格式M x y,意思为将当前位置为x的那些立方体堆叠到当前位置为y的那些立方体上面

2)Count,输入格式 C x,意思为查询当前标号为x的立方体下面堆叠了多少个立方体(不包含它自身)

输入的每个操作都为以上两种操作之一,保证不会让立方体从自己的位置移动到自己的位置。

 

乍看这个题目会怎么想?要不要用N个栈来模拟这个过程?

算法上是可行的,不会MLE,但是会TLE……所以不是一个高效的算法,必定会有更好的算法。

 

——于是我们可以想到并查集。时间空间都是允许的。

这道题怎么用并查集呢?

接触过并查集的同学可能还记得一个题目叫做“银河英雄传说”,其实仔细看来题目几乎是一样的,如果做过那道题那么这道题也就没问题了。

下面开始思考算法:

1)因为题目隐含着合并操作,所以当x移动到y时,就让f[x]=y,合并这两个集合,f[x]表示x的父指针

2)但是仅仅这样是不够的,毕竟题目要求的是某个立方体下的立方体个数,因此用b[i]表示以i为根的树的结点总数,初始值为1。用c[i]表示立方体i在当前位置的相对高度(相对最下面的那个立方体),初始为0

对于指令:

ch n1 (n2)

1.如果ch=’M’:将立方体n1所在的那个位置的立方体叠放在立方体n2所在位置的上面

   p1=find(n1)  //找到n1的根

   p2=find(n2)  //找到n2的根

   f[p1]=p2 //合并p1和p2:p1的父指针指向p2;以p1为首的那些立方体叠放在以p2为首的立方体上面

   c[p1]=b[p2]  //修改立方体p1在p2位置的相对高度

   b[p2]=b[p2]+b[p1] //修改以p2为根的树的总结点数:即立方体p2所在位置的立方体数量

2.如果ch=’C’ 输出c[n1] n1的相对高度即n1下面的立方体总数,就是需要的结果

为了防止TLE,需要路径压缩,在路径压缩时需要修改c的值

[pascal 代码]

Const
        maxn=500000;
VAR
        fa,b,c:array[0..maxn]of longint;
Function find(x:longint):longint;
 begin
        if fa[x]=0 then exit(x);
        if fa[fa[x]]=0 then exit(fa[x]);
        find:=find(fa[x]);
        c[x]:=c[x]+c[fa[x]];
        fa[x]:=find;
 end;
Procedure work;
 var
        n1,n2,p1,p2:longint;
        ch:char;
        i,k:longint;
 begin
        readln(k);
        for i:=1 to k do
                begin
                        read(ch);
                        if ch='M' then
                                begin
                                        readln(n1,n2);
                                        p1:=find(n1);
                                        p2:=find(n2);
                                        fa[p1]:=p2;
                                        c[p1]:=b[p2];
                                        b[p2]:=b[p2]+b[p1];
                                end
                        else
                                begin
                                        readln(n1);
                                        find(n1);
                                        writeln(c[n1]);
                                end;
                end;
 end;
Procedure init;
 var
        i:longint;
 begin
        for i:=1 to maxn do
                begin
                        fa[i]:=0;
                        b[i]:=1;
                        c[i]:=0;
                end;
 end;
Begin
        init;
        work;
End.
posted @ 2011-10-26 17:06  NoRush  阅读(265)  评论(0编辑  收藏  举报