これはなに?
Min_25さんの以下の記事を自分が読みやすいように書き換えたものです。
min-25.hatenablog.com
Min_25さんによる実装はこちら
https://ideone.com/9F9amv
要求知識
・平方根の上下で分けるテク
・fenwick tree
・線形篩の"気持ち"を知っていると理解の助けになる箇所が一部ある
定義
定義域が正整数全体である複素数値関数 f が次を満たすとき、f は乗法的(multiplicative)であるという。
定義から、f≠0 が multiplicativeなら f(1)=1 であることに注意。以下単に f とかいたとき、それは multiplicative であるとする。
以下では値域が複素数であることは使っていないので、演算がO(1)でできる環なら同様に計算できるはず。
記号の定義
・ 素数のとこだけの和
・ 全部の和
・ : x の最小素因子。
・ : k番目の素数。1-based index
・ : x以下の素数の個数
・ 最小素因子がでかいとこだけ生き残るやつ
・ 最小素因子がでかいとこだけの和。 に注意。
・
・
については N が明らかな時には単にH,Lと表す。
何ができるか
任意の素数べき に対し は から で計算可能であるとする。
全ての に対して が求まっているとき、全ての に対する を時間計算量、空間計算量で求めることができる。
Q. 元記事に比べて仮定が強すぎない?(元記事では の計算からやっているはず)
A. の計算には本編と同じようなテクを使うので、説明の順番的に後回し
という概念を用意したことからお察しのとおり、kが大きいところから順に を計算していって、最終的に にたどり着きます。
前処理として 以下の素数を列挙しておきます。
本編は3つのパートからなります。
お断り:この記事は気持ちを理解することを優先しているため、境界条件については適当です。正確な部分は元記事を見るか自分でよく考えて実装しましょう。
に対して を求める
要するに、 より大きな素因子しか持たない部分について計算しましょうという話です。、 とします。
を満たすような i は「1」「素数」「素数の2乗」「相異なる2つの素数の積」のいずれかです。ということで、 は を使うことで簡単に計算できます。
具体的には x の値に応じて次の通りです。
・ のとき
の範囲には となる i は「1」しか存在しません。
よって です。
・ のとき
の範囲には となる i は「1」「素数」しか存在しません。
よって です。
・ のとき
の範囲には となる i は 「1」「素数」「素数の2乗」「相異なる2つの素数の積」しか存在しません。
よって です。
第4項は 「p'×(p'よりでかい素数p'')」の形で表される数について、p''で和を取ったものです。
計算量解析
上2つのケースは各xに対しO(1)です。そもそもxの候補が全体で 個しかないので、この部分は合わせて です。
3つ目のケースは各xに対し です。xとして を計算するので、
です。
空間計算量は、 の値の保持以外に領域を使っていないため、明らかに です。
に対して を求める
ここが一番難しい。
さっきは まででしたが今度は までやります。 から を作るというステップを順番に刻んでいて まで行きます。更新はin-placeにやることにします。
説明のためだけに新たな記号を用意します(えぇ……)。
に対し、 とします。 に入ってるやつで x の次に小さい値ですね。ただし、のときとします。
が 以上か以下かで更新の仕方を変えるんですが、そもそも 以上か以下かで値の持ち方も変えます。
つまり、今までは、各 に対して を持っていましたがそれをやめて、 のときには、一旦 を持つことにします。 のときは今まで通り を持ちます。
そうして、の部分をfenwick treeで管理します。これで今まで通りを取ってこれます。
要するに、「点更新クエリが飛んでくるなら累積和じゃなくてfenwick treeかなーやっぱりw」です。
更新の仕方を考えましょう
・ のとき
「もらうDP」の気持ちで更新します。
を使って愚直に計算するだけです。
のときの の値はfenwick treeにrange sumクエリを投げて取ってくることになるのでlogがつくことに気をつけましょう。
・ のとき
「配るDP」の気持ちで更新します。
まずは x=1から初めて に配ります。そうしたら、配った先の地点で、今度は更に に配ります。線形篩と同じですね。
配る時にはfenwick treeにpoint addクエリを投げることになるのでlogがつくことに気をつけましょう。
以上の範囲は上述の通り別に計算しているので、配る範囲は 未満までであることにも気をつけましょう。
配り元としては までを考えればいいので、 の値が問題なくとれることにも注意しましょう。
初手で配り先は 以上になるので、2個目以降の素数としては、 以下のみを考慮すれば良く、事前に列挙した範囲で収まることにも気をつけましょう。
気をつけることがいっぱい。
計算量解析
ここも難しい
・ の範囲について
面倒なのでfenwick treeにクエリを投げなくていいところも投げてしまうことにすると、クエリを投げる回数の総和は
となります。
に気をつけて変形すると となるので、クエリ1回に かかることから、全体の計算量は です。
・ の範囲について
配る先がいくつあるかがクエリを投げる回数になります。
配る先は、lpf が 以上であるような 未満の数全てです。そのような数は 個らしいので*1、クエリ1回に かかることから、全体の計算量は です。
の計算時には の値の一部をfenwick treeで持っていますが、定数倍の違いしかないので空間計算量は です。
に対して を求める
を使って愚直に計算するだけです。やるだけ。
計算量解析
k を1つ進めるために、各 xに対し、 回の計算が必要なので、全体で
くらいの更新が必要で
になる。
の計算時には 以外の値を保持していないので空間計算量は です。
結局 は?
TODO:書く
2<=p<=N^{1/6}は (Lucy) DP で O(N^{2/3}/log(N))。
— Min_25 (@min_25_) 2020年4月7日
N^{1/6}<p<=N^{1/3} は、 N^{2/3} 未満は Fenwick で愚直更新で O(N^{2/3}/log(N)*log(N))、N^{2/3} 以上は DP + Fenwick で O(N^{1/3}/log(N)*N^{1/3}*log(N))。
N^{1/3}<p<=N^{1/2} は、Lucy DP で O(N^{2/3}) (N^{2/3}/log(N)) (?) 想定でした。
Lucy DPというのは prime counting でおなじみのやつです(多分)