数列をヒストグラム化することで解決できるタイプの問題!特に今回みたいに、数値の値も 以下と小さい場合はすごくそれっぽい!
問題概要
長さが の正の整数からなる数列 が与えられる。以下の条件を満たす の個数を求めよ。
- なる任意の に対して、 は の倍数ではない
制約
考えたこと
数列 を次のような配列に変換して考えるのは、割とよくやっても良さそうな気がする。
num[v]
← 数列 の中に が何個あるか
このように変換したとき、サイズ に関する問題から、サイズ に関する問題へと変わる ( を登場する最大値とする)。このように変換してあげた方が解きやすいことも多い。
さて、今回の問題を解くにあたっては、エラトステネスの篩と同じような方法でやると良さそう。こんな感じ。
int V = 1000000; vector<bool> ok(V, true); for (int d = 1; d < MAX; ++d) { if (v[d] == 0) continue; if (v[d] > 1) v[d] = 0; // d の倍数を除去していく for (int e = d * 2; e < MAX; e += d) v[e] = 0; }
これは一見すると かかるように見えるかもしれない。しかしちゃんと解析すると になっているのだ。計算時間をよりちゃんと書くと
という風になる。二番目の for 文は の倍数のみを走るので、大体 くらいの範囲を動くことになるのだ。一方
となることが知られている ( の積分が であることから来る)。よって、上述の処理の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; const int MAX = 2100000; int main() { int N; cin >> N; vector<long long> v(MAX, 0); for (int i = 0; i < N; ++i) { long long a; cin >> a; v[a]++; } for (int d = 1; d < MAX; ++d) { if (v[d] == 0) continue; if (v[d] > 1) v[d] = 0; for (int e = d * 2; e < MAX; e += d) v[e] = 0; } long long res = 0; for (int d = 1; d < MAX; ++d) res += v[d]; cout << res << endl; }