難易度評価

難易度を1〜10段階で評価できます。投票はあとから変更・取り消しできます。

難易度(投票) Lv.4.0 (1票)

投票の分布
  1. Lv.10票
  2. Lv.20票
  3. Lv.30票
  4. Lv.41票
  5. Lv.50票
  6. Lv.60票
  7. Lv.70票
  8. Lv.80票
  9. Lv.90票
  10. Lv.100票

難易度の目安を見る ↗

ログインして難易度を投票

外部サイトへ移動します

リンク先の URL を確認してから開いてください。

別タブで開く

Non-Coprime Connection Graph

作成者 frugq

難易度(作成者設定) Lv.4

難易度(投票) Lv.4.0 (1票)

実行時間制限
2 秒
メモリ制限
512 MiB

解説

素数の値を持つ頂点とそれらをつなぐ辺からなるグラフを考えます。

AiA_iの素因数がp1,p2,⋯pkp_1, p_2, \cdots p_kであるとき、p1,p2,⋯pkp_1, p_2, \cdots p_kの値を持つ頂点が連結になるように辺を張ればよいです。 また、連結成分に対して頂点数を持たせ適切に更新することで、頂点iiと連結な頂点数を求められます。

これはUnion-Find木を用いることで実装できます。 p1p_1とp2p_2、p1p_1とp3p_3 …\dots p1p_1とpkp_kの値を持つ頂点間に辺を追加すれば、それらの点は連結になります。連結成分ごとの頂点数はUnion-Find木の代表元(leader)に持たせればよいです。

Ai=1A_i=1のとき、辺は追加されないことに注意してください。

C++のサンプルコード

#include <iostream>
#include <vector>
#include <atcoder/dsu>
using namespace std;
using namespace atcoder;
using ll = long long;

const ll maxA = 1e6 + 1;
vector<ll> minPrimeFactor(maxA, 1);
vector<ll> id(maxA, -1);

//xの素因数を列挙
vector<ll> calcPrimeFactors(ll x){
    vector<ll> result;
    while(x > 1){
        result.push_back(minPrimeFactor.at(x));
        while(x % result.back() == 0){
            x /= result.back();
        }
    }
    return result;
}

int main(){
    //エラトステネスの篩
    int nowId = 0;
    for(ll i = 2; i < maxA; ++i){
        if(minPrimeFactor.at(i) == 1){
            minPrimeFactor.at(i) = i;
            nowId++;
            id.at(i) = nowId - 1;//素数→素数の番目
            for(ll j = 2 * i; j < maxA; j += i){
                if(minPrimeFactor.at(j) == 1){
                    minPrimeFactor.at(j) = i;
                }
            }
        }
    }

    ll Q;
    cin >> Q;
    vector<ll> A(Q);
    vector<ll> ans;
    for(int i = 0; i < Q; ++i){
        cin >> A.at(i);
    }

    //素数の値を持つ頂点からなるグラフの連結をunion findで判定
    dsu uf(nowId);
    vector<int> cntVertexes(nowId, 0);
    for(int i = 0; i < Q; ++i){
        if(A.at(i) == 1){
            ans.push_back(1);
            continue;
        }
        auto vec = calcPrimeFactors(A.at(i));
        vector<int> ids;
        for(ll prime : vec){
            ids.push_back(id.at(prime));
        }
        for(int j = 1; j < (int)vec.size(); ++j){
            int u = uf.leader(ids.at(0));
            int v = uf.leader(ids.at(j));
            if(!uf.same(u, v)){
                uf.merge(u, v);
                cntVertexes.at(uf.leader(u)) = cntVertexes.at(u) + cntVertexes.at(v);
            }
        }
        int top = uf.leader(ids.at(0));
        cntVertexes.at(top)++;

        ans.push_back(cntVertexes.at(top));
    }
    for(int i = 0; i < Q; ++i){
        if(i != 0) cout << " ";
        cout << ans.at(i);
    }
    cout << endl;
    return 0;
}

ユーザー解説

ユーザー解説を書く

ユーザー解説はまだありません。