解説
素数の値を持つ頂点とそれらをつなぐ辺からなるグラフを考えます。
の素因数がであるとき、の値を持つ頂点が連結になるように辺を張ればよいです。 また、連結成分に対して頂点数を持たせ適切に更新することで、頂点と連結な頂点数を求められます。
これはUnion-Find木を用いることで実装できます。 と、と との値を持つ頂点間に辺を追加すれば、それらの点は連結になります。連結成分ごとの頂点数はUnion-Find木の代表元(leader)に持たせればよいです。
のとき、辺は追加されないことに注意してください。
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;
}
ユーザー解説
ユーザー解説を書くユーザー解説はまだありません。