AtCoder NoviStepsを埋めてみる(7) 再帰全探索 1Q以降の続きです。今回はエラトステネスの篩です。

エラトステネスの篩とは?

エラトステネスの篩は、N 以下の正の素数を O(N loglog N) の計算量で列挙するアルゴリズムです。古代ギリシアの科学者、エラトステネスが考案したといわれています。

1 ~ n までの数字を書く
1 は素数ではないので消す
残った数のなかで最小の 2 は素数。それ以外の 2 の倍数は素数ではないので消す。
残った数のなかで次に最小の 3 は素数。それ以外の 3 の倍数は素数ではないので消す。
残った数のなかで次に最小の 5 は素数。それ以外の 5 の倍数は素数ではないので消す。
これを繰り返すと最終的に素数のみが残る。

篩い落とし操作は n の平方根 までやれば充分です。これだと n の平方根 より大きな素数による合成数を消すことができませんが、これは n よりも大きな値となるので気にする必要はありません。

あとこんなメソッドも定義しておきます。

B26 – Output Prime Numbers

B26 – Output Prime Numbers

問題の概要

N 以下の素数を値の小さい方から全て出力せよ。

これは基本問題です。

D – 2017-like Number

D – 2017-like Number

問題の概要

N も (N + 1) ÷ 2 も素数であるという条件を満たす奇数 N を 2017に似た数 と定義する。
Q 個のクエリで l[i], r[i] が与えられる。
l[i] 以上 r[i] 以下の 2017に似た数 となる奇数 の個数を求めよ。

ある奇数が 2017に似た数かどうかはエラトステネスの篩で得た配列を見ればわかるのですが、l[i] 以上 r[i] 以下の 2017に似た数 となる奇数 の個数をクエリのたびに数えていては時間がかかります。

そこで前処理として 10^5以下の整数について 2017 に似た数かどうかを調べるとともに sums[i] に i 以下の 2017 に似た数の個数を格納しています。クエリがきたら sums[r] – sums[l – 1] を計算することですぐに解を返せるようにしています。

D – 250-like Number

D – 250-like Number

問題の概要

k が素数 p < q であるふたつの素数を使って k = p × q^3 と表されるとき、k を「 250 に似た数」と定義する。 N 以下の 250 に似た数 は全部でいくつあるだろうか? N が 10^18 と大きいのですが、q が取りうる範囲も 10^6 なので全探索可能です。 q を全探索します。N を q^3 で割ってみてその整数部分と q - 1 を比較します。両者のうち小さいほうが p の最大値となります。 エラトステネスの篩で 10^6 以下の素数を全取得するとともに i 以下の素数の個数をすぐに求めることができるように counts[i] に i 以下の素数の個数を格納しておきます。

D – Coprime 2

D – Coprime 2

問題の概要

長さ N の正整数列 A が与えられる。
すべての要素と互いに素である 1 以上 M 以下の整数をすべて求めてください。

A のすべての要素がもたない素因数と、これらを掛け合わせて生成される合成数が出力すべき解です。

A のそれぞれの要素を素因数分解しようとすると時間がかかりそうですが、工夫すると短時間で終わります。

1 から M までの整数で取得できた素因数で割ることができるかどうかの処理もひとつひとつ確認していては時間がかかるのでエラトステネスの篩のような処理をすることで時間短縮しています。

まず素因数分解をするためのクラスを定義します。

E – Coprime

E – Coprime

問題の概要

長さ N の数列 A が与えられます。
① すべての 0 ≦ i, j ≦ N – 1 について、GCD(A[i] ,A[j]) = 1 が成り立つか?
② ① が成り立たないとき、GCD(A[0], A[i], …, A[N – 1]) = 1 は成り立つか?
① が成り立つときは pairwise coprime、② が成り立つときは setwise coprime、どちらでもない場合は not coprime と出力せよ。

① である場合はそれぞれの要素が共通する素因数を持たないことを意味しています。なので、それを確認すればよいです。② かどうかは実際に最大公約数を計算すればよいです。

最大公約数を求める処理はユークリッドの互除法を用いています。

D – 9 Divisors

D – 9 Divisors

問題の概要

N 以下の正整数のうち、正の約数をちょうど 9 個持つものの個数を求めよ。

素因数分解したときに A^a × B^b × C^c × … となる自然数の約数の個数は (a + 1) × (b + 1) × (c + 1) × … です。

では (a + 1) × (b + 1) × (c + 1) × … が 9 になる場合とはどのような場合でしょうか? a = 8 のときと、a = 2, b = 2 のとき以外ありません。

a = 8 のときは比較的簡単です。エラトステネスの篩で素数を取得してそれらの 8 乗が N を超えるまで数え上げます。a = 2, b = 2 の場合は実は同じ素数の組み合わせをダブルカウントしないように 2 つの素数 p, q を p < q と考えます。素数の 2 乗のリストを生成して、すべての p について N / (p * p) 以下で (p * p) + 1 以上の個数を二分探索法で数え上げます。p が大きくなると N / (p * p) が p を下回ったり、N / (p * p) 以下で (p * p) + 1 以上の要素の個数が 0 になります。その場合はそれ以上調べても見つからないので探索を終了します。

二分探索の処理は ライブラリ ac-library-csharp の StlFunction.BinarySearch メソッドを使用しています。

参考: ac-library-csharpを使ってみる

D – AABCC

D – AABCC

問題の概要

N 以下の正整数のうち、a < b < c なる 素数 a, b, c を用いて a^2 × b × c^2 と表せるものはいくつあるか求めよ。

N が 10^12 と大きいですが、a < b < c という条件があるため、a を大きくしたらそれにともなって c も大きくするしかなく、意外と探索範囲は狭いです。

097 – Primes in an Interval

097 – Primes in an Interval

問題の概要

L 以上 R 以下の素数の個数を求めよ。

これはエラトステネスの区間篩です。L と R が 10^12 に近い大きな数ですが、これらの数が合成数であれば 10^6 の素因数をもつため、エラトステネスの篩で 10^6 までの素数を求めて、この倍数をふるい落とすことで素数の個数を得ることは可能です。

まずエラトステネスの区間篩のためのクラスを定義します。