tokizo

競技プログラミングの備忘録や日記

HackerRank Journey to the Moon

気持ちが良い問題だった
問題リンク

問題概要

N人の宇宙飛行士がいます
P個のペアが渡されます
ペアは同じ国籍同士の宇宙飛行士の番号です
N人の中から,国籍が違う宇宙飛行士のペアはいくつあるか

解説

同じ国籍どうしの宇宙飛行士をDFSやUnionFindでグループを作ります
例えばAグループに3人,Bグループに4人いるとすると,Aグループ3人それぞれに対しBグループの4人がペアになりうるので生成できるペア数は12となります

愚直に二重for分でペア数を掛けていくと,時間がかかるのは予想できます

そこで,ひとまずN人から2人を選ぶ組み合わせの総数を出します
そこから同じグループから2人を選ぶ組み合わせの数をグループごとに見ていき引いていけば,答えが求まります

イメージは余事象的な感じですかね?

コード(C++14)

#include <bits/stdc++.h>
using namespace std;

struct UnionFind{
    vector<long long> par;
    UnionFind(long long N) : par(N){
        for(long long i = 0; i < N; i++){
            par[i] = -1;
        }
    }
    long long root(long long x){
        if(par[x] < 0) return x;
        else return par[x] = root(par[x]);
    }
    void unite(long long x, long long y){
        long long rx = root(x);
        long long ry = root(y);
        if(rx == ry) return;
        else{
            if(par[rx] > par[ry]) swap(rx, ry);
            par[rx] += par[ry];
            par[ry] = rx;
       }
    }
    bool same(long long x, long long y){
        return root(x) == root(y);
    }
    void check(){
        for(auto x : par){
            cout << x << " ";
        }
        cout << endl;
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    long long n, p;
    cin >> n >> p;
    UnionFind uf(n);
    while(p--){
        long long a, b;
        cin >> a >> b;
        uf.unite(a, b);
    }

    map<long long, long long> mp;
    for(long long i = 0; i < n; i++){
        long long r = uf.root(i);
        mp[r] = -uf.par[r];
    }

    vector<long long> B;
    for(auto x : mp){
        B.push_back(x.second);
    }

    long long bs = B.size();
    long long ans = n * (n - 1) / 2;
    for(long long i = 0; i < bs; i++){
        ans -= B[i] * (B[i] - 1) / 2;
    }

    cout << ans << endl;

    return 0;
}