銀河英雄傳說(並查集)

Gnomeshgh___發表於2020-11-15

問題描述

有一個劃分為N列的星際戰場,各列依次編號為1,2,…,N。

有N艘戰艦,也依次編號為1,2,…,N,其中第i號戰艦處於第i列。

有T條指令,每條指令格式為以下兩種之一:

1、M i j,表示讓第i號戰艦所在列的全部戰艦保持原有順序,接在第j號戰艦所在列的尾部。

2、C i j,表示詢問第i號戰艦與第j號戰艦當前是否處於同一列中,如果在同一列中,它們之間間隔了多少艘戰艦。

現在需要你編寫一個程式,處理一系列的指令。

輸入格式
第一行包含整數T,表示共有T條指令。

接下來T行,每行一個指令,指令有兩種形式:M i j或C i j。

其中M和C為大寫字母表示指令型別,i和j為整數,表示指令涉及的戰艦編號。

輸出格式
你的程式應當依次對輸入的每一條指令進行分析和處理:

如果是M i j形式,則表示艦隊排列發生了變化,你的程式要注意到這一點,但是不要輸出任何資訊;

如果是C i j形式,你的程式要輸出一行,僅包含一個整數,表示在同一列上,第i號戰艦與第j號戰艦之間佈置的戰艦數目,如果第i號戰艦與第j號戰艦當前不在同一列上,則輸出-1。

資料範圍
N≤30000,T≤500000
輸入樣例:
4
M 2 3
C 1 2
M 2 4
C 4 2
輸出樣例:
-1
1

思路

這一題是一個帶邊權的並查集。需要我們注意的是,在路徑壓縮的情況下我們需要額外建立一個陣列記錄x到fa[x]之間邊的權值,在路徑壓縮把x直接指向樹根的同時,我們需要把d[x]更新為x到樹根的路徑上所有邊的權值的之和。
另一個要注意就是,將x的樹根作為子節點合併到y的樹根的時候,這條新邊的權值要改為之前y集合的大小,因為y中所以戰艦都在x之前。

AC程式碼

#include<iostream>

using namespace std;

const int N = 30010;

int n, m;
int fa[N], s[N], d[N];

int get(int x) {
    if (x == fa[x]) return x;
    int root = get(fa[x]);
    d[x] += d[fa[x]];
    return fa[x] = root;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    n = 30000;
    cin >> m;
    
    for (int i = 1; i <= n; i ++) {
        fa[i] = i;
        s[i] = 1;
    }
    
    while (m --) {
        char op[2];
        int x, y;
        
        cin >> op >> x >> y;
        if (op[0] == 'M') {
            x = get(x), y = get(y);
            d[x] = s[y];
            s[y] += s[x];
            fa[x] = y;
        }
        else {
            if (get(x) != get(y)) cout << -1 << endl;
            else cout << abs(d[x] - d[y]) - 1 << endl;
        }
    }
    
    return 0;
}

相關文章