CodeChefのフォーラムで June Challenge 2017の問題 Chef and Prime Queries (PRMQ) の解法を読んでいて

discuss.codechef.com

素因数分解をするのに

This can be done by creating a Smallest Prime Factor array in the sieve function itself.

とあって。エラトステネスの篩 (Sieve of Eratosthenes) をするときに、篩と同時に一番小さい素因数のテーブルを作ってしまえば素因数分解が簡単、っていう話。

篩の配列を一番小さい素因数を表すテーブルにしてしまえばいいよね。

（なんで今までそうしてこなかったんだ…今まで愚直に、2から までの素数でnを割り切れるかいちいち１つずつ試してた。これなら100倍ぐらい速そう）

#include <vector> using namespace std; vector<int> primes; vector<int> smallest_prime_factor; int sieve(int nmax){ primes.clear(); smallest_prime_factor.assign(nmax+1, 0); // ここではあえて愚直な書き方 for (int n=2; n<=nmax; ++n) { if (!smallest_prime_factor[n]) { primes.push_back(n); for (int kn=n; kn<=nmax; kn+=n) { if (!smallest_prime_factor[kn]) { // ←このチェックをせずに上書きを続けると largest_prime_factor[] が出来上がる smallest_prime_factor[kn] = n; } } } } return primes.size(); } // xを素因数分解 vector<int> factorize(int x) { vector<int> prime_factors; while (x > 1) { int s = smallest_prime_factor[x]; if (!s) break; prime_factors.push_back(s); x /= s; } return prime_factors; }

例えば x=120 の場合、

smallest_prime_factor[120] = 2 120 / 2 = 60 smallest_prime_factor[60] = 2 60 / 2 = 30 smallest_prime_factor[30] = 2 30 / 2 = 15 smallest_prime_factor[15] = 3 15 / 3 = 5 smallest_prime_factor[5] = 5 5 / 5 = 1

なので、factorize(120) は vector<int> {2,2,2,3,5} を返す。

nが100万までなら prime_factors[] の長さは高々19 で、

使われている素数は高々7個

本番中に書いたPRMQのコードの素因数分解部分をこれに差し替えてみたら、N=100000のケースで10〜20msecほど縮まった。

でもlargeでTLEを出してた（segment tree導入前の）コードは、1.3秒ほどかかる手元の最悪ケースで10msec縮んでもどうしようもない。