我這一題是用
DSU 結構
p[i]:並查集父指標,初始 p[i] = i。
has_special[i]:用來標記以「代表元 ii」為根的集合內,是否含有至少一個巨人城鎮。
讀入與標記巨人
先讀 N,M,KN,M,K。
讀 KK 個住巨人的城鎮編號 aiai,直接把 dsu.has_special[a_i] = true。
桶排序
因為 1≤c≤100001≤c≤10000,所以用 buckets 這種 vector of vector,只要額外開 1000110001 個桶,讀完每條邊就 buckets[c].push_back({u,v})。
這步只要 O(M)O(M),沒有額外的 logMlogM 開銷。
按權重掃描
由 w=1w=1 掃到 w=10000w=10000,若 buckets[w] 裡有邊,就依序取出邊 (u,v)(u,v):
找出 ru = find(u)、rv = find(v)。
如果同根或兩集合都已有巨人,就跳過。
否則 unite(ru, rv),並把 ww 加到 answer。
時間複雜度
桶排序「將 MM 條邊放桶」: O(M)O(M)。
然後「遍歷 1000010000 個桶」:掃過每個桶的所有邊,共 MM 條,做 MM 次 find/unite,每次近似 O(α(N))≈O(1)O(α(N))≈O(1)。
因此整體約 O(M+10000+Mα(N))≈O(M)O(M+10000+Mα(N))≈O(M),遠快於 O(MlogM)O(MlogM) 或 O(MlogN)O(MlogN)。
這是我第二次優化的地方
扁平計數排序(Counting Sort):一次性把所有邊根據權重 1…100001…10000 分到「連續陣列區段」,避免 std::vectorstd::vector 的額外開銷,也省去 O(MlogM)O(MlogM)\ 排序。
DSU(並查集)用「按集合大小合併 + 路徑壓縮」,確保 α(N)α(N) 時間。
自訂快速輸入(FastIO),避免 cincin 或 scanfscanf 的額外負擔。