题目的大意是给你一些相同的立方体,共有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.