經過分析,不難知道本題需要維護乙個資料結構,使之支援以下操作:
1. 合併戰艦集合(m i j);
2. 查詢2艘戰艦是否在同一集合/列(c i j);
3. 如果在同一集合,求出它們的相對位置。
標準的並查集只支援合併和查詢操作,並不支援求集合中兩元素之間相對距離的操作。所以我們要用擴充套件並查集。標準並查集中每個節點只有乙個域,即用father[i] 表示節點 i 的父親。本題可以擴充套件出兩個域:
用total[i]表示以 i為根的集合中的元素個數,它當且僅當i是根時有意義;
用front[i]表示排在 i前面有多少個點,front[root]=0。
若合併操作令 father[a] = b(a和 b都是根),那麼同時必須令:
front[a] = total[b];
total[b] = total[b] + total[a];
ps:total[a]不需要更新,因為它已經不再是根節點了;front[b]也不需要更新,因為沒有發生變化。
執行查詢操作時,由於每個集合的根節點都沒有發生變化,所有不需要改變total域,只需要修改front域。執行find(i)時,要將節點i到根節點路徑上的所有必經點的front累加起來,作為該點新的front值。因為front值從某種意義上來說,表示的是乙個點失去「根節點」的地位的那一刻其前面有多少個點。
比如有8個點:
依次執行:合併1和2;合併3和4;合併5和6;合併7和8。
再依次執行:合併1和3;合併5和7。
再合併1和5。
樹的形狀:
佇列中節點依次應是:1、2、3、4、5、6、7、8。
觀察各個節點的 front域,發現它們都停留在該節點失去「根節點」地位那一瞬間的狀態,記錄的是那一瞬間使它喪失「根節點」地位的子樹一共有多少個點,也即那一瞬間排在它前面的有多少點。
如果執行 find(6),執行前:
front(6) = 1,對應著節點6前面的節點5;
front(5) = 4,對應著節點5前面的節點1~4;
front(1) = 0,意味著1就是根節點。
執行find(6),會導致front(6) = front(6) + front(5) + front(1) = 5,對應著節點6前面的節點1~5。
如果再執行一次find(6),front(6) = front(6) + front(1) = 5,還是不變。
【c++版本】
#include int fa[30001]; // i 的父親,初始值為 i 自己【pascal版本】int s[30001]; // i 所在佇列中 i 前面元素的個數,初始值為 0
int t[30001]; // i 所在佇列中所有元素總個數,初始值為 1
int m;
inline int abs(int a)
int find(int v)
p = j;
} return root;
}int main()
for (i=1; i<=m; i++)
}else
}return 0;
}
// 140k 1136ms pascal 1.36k// d[i]表示它在它所處集合中的深度(即離根的距離),d[root]=0;
// l[i]表示以i為根的集合中的元素個數,它當且僅當i是根時有意義。
const
maxn=30000;
type
node=record
p,d,l :integer;
end;
var uf :array[1..maxn]of node; //並查集
n,m :longint;
a,b,i :longint;
c :char;
procedure init;
var i :integer;
begin
//assign(input,'galaxy.in');reset(input);
//assign(output,'galaxy.out');rewrite(output);
n:=30000;
readln(m);
for i:=1 to n do //初始化並查集:p[i]=i,l[i]=1
with uf[i] do begin p:=i; l:=1 end;
end;
function find(x:integer):integer;
var i,j,p,q :integer;
begin
i:=x;
while uf[i].p<>i do i:=uf[i].p; //查詢代表元
p:=i;
i:=x;
while i<>p do
begin
q:=uf[i].p;
uf[i].p:=p; //路徑壓縮
j:=q;
repeat
inc(uf[i].d,uf[j].d); //更新d值
j:=uf[j].p
until uf[j].p=j;
i:=q;
end;
find:=p;
end;
procedure union(a,b:integer);
var t :integer;
begin
a:=find(a); b:=find(b);
uf[a].p:=b; //合併
uf[a].d:=uf[b].l; //更新d
inc(uf[b].l,uf[a].l) //更新l
end;
begin
init;
for i:=1 to m do
begin
readln(c,a,b);
if c='m' then union(a,b);
if c='c' then
if (find(a)=find(b)) then
writeln(abs(uf[a].d-uf[b].d)-1)
else writeln(-1)
end;
//close(output)
end.
銀河英雄傳說
公元五八 一年,地球居民遷至金牛座 第二行星,在那裡發表銀河聯邦創立宣言,同年改元為宇宙歷元年,並開始向銀河系深處拓展。宇宙歷七九九年,銀河系的兩大軍事集 在巴公尺利恩星域爆發戰爭。泰山壓頂集 宇宙艦隊司令萊因哈特率領十萬餘艘戰艦出征,氣吞山河集 點名將楊威利組織麾下三萬艘戰艦迎敵。楊威利擅長排兵布...
銀河英雄傳說
公元五八 一年,地球居民遷至金牛座 第二行星,在那裡發表銀河聯邦創立宣言,同年改元為宇宙歷元年,並開始向銀河系深處拓展。宇宙歷七九九年,銀河系的兩大軍事集 在巴公尺利恩星域爆發戰爭。泰山壓頂集 宇宙艦隊司令萊因哈特率領十萬餘艘戰艦出征,氣吞山河集 點名將楊威利組織麾下三萬艘戰艦迎敵。楊威利擅長排兵布...
vijos P1443 銀河英雄傳說
並查集 瞎搞 思路 對於合併和查詢是否存在於同一集合好說 關鍵就是查詢之間的戰艦數量的問題 可以利用樹上字首和的思想來實現 用sum i 表示i節點到根的距離 那麼考慮維護 在a隊接到b隊的過程中 需要維護a的節點到根的距離 很顯然a中所有節點的sum需要加上b中節點的個數 那就再開乙個tot i ...