公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。
宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。
杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, …, 30000。之后,他把自己的战舰也依次编号为1, 2, …, 30000,让第i号战舰处于第i列(i = 1, 2, …, 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为M i j,含义为让第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。
然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。
在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,杨威利的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。
作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。
最终的决战已经展开,银河的历史又翻过了一页……
4
M 2 3
C 1 2
M 2 4
C 4 2
-1
1
本题为NOI2002年的题目,这道题目很明显需要使用并查集,因为题目中说了将一列上的战舰调到另一列上去,然后对于每一个询问,寻找询问的两艘战舰中间所隔的战舰数,一开始的想法是不用路径压缩的并查集。但后面发现这种形式有问题(你需要去找这一列的第一艘战舰的编号,再将其合并到另一列上),这样的时间复杂度会高一些,不过似乎也能过……这里我采用的是多开两个数组,一个记录该战舰所在列的总战舰数,另一个是记录该战舰前面排了多少个战舰,这样一来,如果询问的两艘战舰在同一列上,我们就只需要把两艘战舰前的战舰数相减去绝对值再减一就行了。那么主要问题就是这两个数组在路径压缩和合并的时候应该如何处理,详见代码。
1 var father,before,count:array[1..30000] of longint; 2 i,n,x,y:longint; 3 ch:char; 4 function getfather(v:longint):longint; 5 var f:longint; 6 begin 7 if father[v]=v then 8 exit(v) 9 else 10 begin 11 f:=getfather(father[v]); 12 before[v]:=before[father[v]]+before[v];//路径压缩,在该战舰前的战舰数为它的祖先前的加上它本身前面的,可自己手推一下。 13 father[v]:=f; 14 exit(father[v]); 15 end; 16 end; 17 procedure union(u,v:longint); 18 var fu,fv:longint; 19 begin 20 fu:=getfather(u); 21 fv:=getfather(v); 22 father[fu]:=fv; 23 before[fu]:=before[fu]+count[fv];//因为两列合并了,所以在fu前面的要加上整个fv所在列的战舰数 24 count[fv]:=count[fv]+count[fu];//fv所在列的战舰数需要加上fu所在的战舰数 25 end; 26 procedure queue(u,v:longint); 27 begin 28 if getfather(u)<>getfather(v) then 29 writeln(-1) 30 else 31 writeln(abs(before[u]-before[v])-1); 32 end; 33 begin 34 readln(n); 35 for i:=1 to 30000 do 36 begin 37 father[i]:=i; 38 before[i]:=0; 39 count[i]:=1; 40 end;//初始化 41 for i:=1 to n do 42 begin 43 readln(ch,x,y); 44 case ch of 45 'M': union(x,y); 46 'C': queue(x,y); 47 end; 48 end; 49 end.