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.