Official

C - 噂の拡散 / Spread of Rumors Editorial by MMNMM


生徒を頂点、友人関係を辺としたグラフにおいて、最初に情報を伝えられた \(K\) 人の生徒のいずれかを含む連結成分の大きさの合計を求めることができれば、この問題を解くことができます。

これは、Union Find(素集合データ構造)や DFS/BFS を用いることで解くことができます。 すでに計算した連結成分についてふたたび計算しないように注意することで、十分高速になります。

また、最初に情報を伝えた \(K\) 人の生徒の間に適切な辺を張り、これらの生徒が属する連結成分を合併しておくことで、単一の連結成分の大きさを求める問題に読み替えることもできます。

時間計算量は DFS/BFS を使う場合 \(O(N+M)\) などに、Union Find を使う場合 \(O((N+M)\alpha (N))\) などとなります。

実装例は以下のようになります。 以下の実装例では、最初に情報を伝えた \(K\) 人をまとめた連結成分を作る方針で解いています。

#include <iostream>
#include <atcoder/dsu>
using namespace std;

int main() {
    int N, M, K, S1;
    cin >> N >> M >> K >> S1;
    --S1; // 伝えられた最初のひとりとそれ以外に分ける

    atcoder::dsu union_find(N);
    for (int i = 1; i < K; ++i) { // 伝えられた残りの生徒について
        int S;
        cin >> S;
        --S;
        union_find.merge(S1, S); // 最初のひとりと結ぶ
    }

    for (int i = 0; i < M; ++i) { // 友人関係について
        int u, v;
        cin >> u >> v;
        --u;
        --v;
        union_find.merge(u, v); // 2 人を結ぶ
    }

    cout << union_find.size(S1) << endl; // 伝えられた最初の生徒が属する連結成分の大きさが答え
    return 0;
}
from atcoder import dsu


N, M, K = map(int, input().split())

S1, *S = map(int, input().split()) # 伝えられた最初のひとりとそれ以外に分ける

union_find = dsu.DSU(N)

for s in S: # 伝えられた残りの生徒について
    union_find.merge(S1 - 1, s - 1) # 最初のひとりと結ぶ

for i in range(M): # 友人関係について
    u, v = map(int, input().split())
    union_find.merge(u - 1, v - 1) # 2 人を結ぶ

print(union_find.size(S1 - 1)) # 伝えられた最初の生徒が属する連結成分の大きさが答え

posted:
last update: