https://atcoder.jp/contests/abc170/tasks/abc170_d
前提知識
解説
https://atcoder.jp/contests/abc170/submissions/14362889
この問題は調和級数的計算量を知らないと難しいかもしれない。
つまるところ、
「rep(i,1,N) for(j=i;j<=N;j+=i) というループ構造はO(NlogN)で行える」
というのを知っていれば解ける。
エラトステネスの篩の計算量解析と同じである。
愚直に問題に取り組もうとすると、iの組合せでN通り、それからjの組合せでN-1通りであり、
O(N2)で間に合わない。
iの組合せはそのままで、jを高速化することをまずは考えてみる。
ダメなjは端的に言うと、A[i]の約数であるA[j]があるとダメである。
これを高速に判定したいので、約数を調べてそれがあるかを確かめて…
としたいが計算量が厳しい(実際はうまくやれば通るっぽい)。
なので、この段階でブレイクスルーが必要となる。
約数ではなく倍数を使う事にする。
「iを固定してダメなjを探す」のではなく「jを固定して、これによってダメにするiを探す」ようにする。
これで約数から倍数に考えがシフトした。
たぶん分かりにくいだろうから、ここから解法を直接的に説明する。
最終的に以下の配列を作成する。
ok[x] := A[i]=xである要素が性質を満たすか
これが分かれば、ok[A[i]]=trueであるiの個数が答えになる。
jを固定したときにダメなiというのは、A[j]の倍数であるA[i]となる。
つまり、A[j]が存在すれば、A[j]2,A[j]3,...は全てダメなA[i]となる。
よって、数を[1,106]の範囲でA[j]をチェックして、存在すれば、その倍数は全部ダメなA[i]とする。
このように倍数に対して全探索するような処理は計算量的にはO(NlogN)であり、間に合う。
あとは作成したokを使って判定していく。
注意点として、A[j]が存在するときに複数存在する場合はA[j]もダメなA[i]となるのが注意。
なので、各数毎に個数を数えて利用していくのがいいだろう。
const int MA = 1000001; int N, A[201010]; int cnt[1010101]; bool ok[1010101]; //--------------------------------------------------------------------------------------------------- void _main() { cin >> N; rep(i, 0, N) cin >> A[i]; rep(i, 0, N) cnt[A[i]]++; rep(x, 1, MA) ok[x] = true; rep(x, 1, MA) if(0 < cnt[x]) { if (1 < cnt[x]) ok[x] = false; for (int x2 = x * 2; x2 < MA; x2 += x) ok[x2] = false; } int ans = 0; rep(i, 0, N) if (ok[A[i]]) ans++; cout << ans << endl; }