The cubes(即 银河英雄传说 NOI 2002)
【题目描述】
lynn 和 banana 用 N (1 <= N <= 30,000)块相同的小立方块玩游戏,小方块编号为 1..N。开始时,小方块都单独分开的,每个看成一个柱子,即有 N 柱子。lynn 要 banana 做 P(1 <= P <= 100,000) 个操作,操作有两种类型:
(1) lynn 要求banana 把X 号方块所在的柱子放到 Y 号所在的柱子上面,成一个新柱子。
(2) lynn 要求 banana 计算X 号方块所在柱子,它下面有多少个小方块。
请编个程序,帮助 lynn 计算。
【输入格式】
*第一行:一个整数 P
*第 2..P+1 行:第 i+1 行表示第 i 个 FJ 要求的合法操作。如果这行以'M'开头,后面有两个整
数 X,y 表示要进入(1)操作。 如果这行以'C'开头,后面有一个整数 X,表示要求计算 X 所在
柱子下面的方块个数。
注:所有操作都是合法的。N 并没有出现在输入文件中。
【输出格式】
依次要求计算的值,每次一行。
【输入样例】
6 | 6 个操作
M 1 6 | 1,6 / 2 / 3 / 4 / 5 把 1 放在 6 上面。
C 1 | 输出:1
M 2 4 | 1,6 / 2,4 / 3 / 5
M 2 6 | 2,4,1,6 / 3 / 5
C 3 | 输出 :0
C 4 | 输出: 2
【输出样例】
1
0
2
这个东西,就是把“银河英雄传说”改了改背景。
(但是,银河英雄传说中 C 操作是求两个战舰之间有多少个,这个是求某个柱子前一共有多少个)
开两个数组,数组 a[i]表示 i飞船前面有多少飞船,b[i]表示 i飞船后面有多少飞船。操作 M,将 x, y的祖宗合并,并且进行路径压缩的时候,需要为儿子增加爹前面飞船的数量。
然后,只需要注意一点,每一次输出 C 操作答案时,一定要先执行一下 gf ,让它路径压缩一下子,这样,那个 a[] 才是答案,当然输出的值随便找个变量接着就可以了。这里执行 gf 是为了路径压缩,不是为了判断是否合法,所以不要看他说所有操作都合法就不管了。(maxingc 个混淆视听的家伙)
本来,我更新 a[] 时,用的是 for 循环,超时,后来才发现他可以和路径压缩一块跑,省了 n 多时间复杂度。。。。。。。
代码 Suemiller
program ACRush;
var a,f,b:array[0..30010]of longint;
// v:array[0..30010]of boolean;
i,j,k,p:longint;
s:string;
x,y,code,max:longint;
function gf(x:longint):longint;
var t:longint;
begin
if f[x]=x then exit(x);
t:=f[x];
f[x]:=gf(f[x]);
a[x]:=a[x]+a[t];
exit(f[x]);
end;
procedure union(x,y:longint);
var xx,yy,i:longint;
begin
xx:=gf(x);
yy:=gf(y);
f[xx]:=yy;
a[xx]:=b[yy]+1;
b[yy]:=b[yy]+b[xx]+1;
end;
begin
assign(input,'cubes.in');reset(input);
assign(output,'cubes.out');rewrite(output);
readln(p);
for i:=1 to 30010 do
begin
f[i]:=i;
a[i]:=0;
b[i]:=0;
end;
max:=0;
for i:=1 to p do
begin
readln(s);
case s[1] of
'M':begin
delete(s,1,2);
j:=pos(' ',s);
val(copy(s,1,j-1),x,code);
val(copy(s,j+1,length(s)-j),y,code);
// writeln('M',' ',x,' ',y);
union(x,y);
end;
'C':begin
delete(s,1,2);
val(s,x,code);
// writeln('C',' ',x);
y:=gf(x);
writeln(a[x]);
//银河英雄传说:writeln(abs(a[x]-a[y])-1);
end;
end;
end;
close(input);close(output);
end.