DGZX1521 銀河英雄傳說

2022-05-31 18:21:08 字數 3934 閱讀 2795

經過分析,不難知道本題需要維護乙個資料結構,使之支援以下操作:

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 自己

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;

}

【pascal版本】

// 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 ...