Official

C - FF Editorial by MMNMM


フォロー関係を集合 \(S\) に保持することを考えます。 与えられるクエリは、次のように言い換えられます。 はじめ、\(S=\emptyset\) です。

  1. 集合 \(S\) に順序対 \((A,B)\) を追加する。つまり、\(S\leftarrow S\cup\{(A,B)\}\) とする。
  2. 集合 \(S\) から順序対 \((A,B)\) を取り除く。つまり、\(S\leftarrow S\setminus\{(A,B)\}\) とする。
  3. 順序対 \((A,B),(B,A)\) がいずれも \(S\) に含まれているかを判定する。

入力として与えられる可能性のある順序対は \(N(N-1)\) 通りありますが、集合 \(S\) のサイズは全体を通してたかだか \(Q\) であるため、\(S\) を連想配列などで管理すればこの問題が解けます。

順序対をキーにできない連想配列のライブラリを使っている場合も、順序対 \((A,B)\) と整数 \(AN+B\) を対応させることで整数をキーにした連想配列を使ってこの問題を解くことができます。

時間計算量は、連想配列に平衡二分探索木などを用いると \(O(Q\log Q)\) 時間、ハッシュテーブルなどを用いると期待 \(O(Q)\) 時間です。

実装例は以下のようになります。

#include <iostream>
#include <set>
#include <utility>

int main() {
    using namespace std;

    unsigned N, Q;
    cin >> N >> Q;

    // フォロー関係を保持する集合
    set<pair<unsigned, unsigned>> follows;

    for (unsigned _{}; _ < Q; ++_) {
        unsigned type, A, B;
        cin >> type >> A >> B;
        if (type == 1)
            // 順序対 (A, B) を追加
            follows.emplace(A, B);
        else if (type == 2)
            // 順序対 (A, B) を削除
            follows.erase({A, B});
        else
            // 順序対 (A, B), (B, A) がどちらも含まれるか判定
            cout << (follows.count({A, B}) && follows.count({B, A}) ? "Yes" : "No") << endl;
    }

    return 0;
}

posted:
last update: