ABCでもよく見る典型がいくつか組み合わさった問題。
自然な形で上手く組み合わさっているので、気付くとなかなか気持ちいい。
1人1人が受け取るピースの塊を、「分け前」と呼ぶことにする。
円形のケーキじゃなくて両端があるなら、大枠は典型90問の第1問目でも名高いこの問題に近くなる。
両端が分かっている場合、「全体を、K 個以上の、質量 x 以上の分け前に分割できるか?」が解ければ、x を二分探索することで解ける。
これは端から「ピースの質量合計が x 以上になったらそこですぐ切る」ことを繰り返せば、O(N) で判定できる。
ただ、今回は端が決まっていないので、切り目 1 から切った場合は無理でも、他の切り目から始めたら行けるかもしれない。
i 1 2 3 4 5 K=3 A 6 8 6 4 3 x=8 は可能? 1から始めると、i=1,2 / 3,4 /(残りは8に満たない)で2個。NG 2から始めると、i=2 / 3,4 / 5,1 3個に分けられる。OK
N 箇所全ての切り目から1回 O(N) の判定を試すのは O(N2) で無理。
ただ、どこから始めても、その過程で「i が分け前の開始ピースとなったら、どこまで続いて、次に切られるのはどこ」という関係性は変わらない。
例えば、上例では i=3 から始まった分け前は、共通して 3,4 まで続き、次の分け前は i=5 から開始される。
分け前が i から始まった場合の次の分け前の開始点を x=8,i=3 に対して B8[3]=5 というように表現すると、B8=(3,3,5,7,7) となる。(1周を超える分でも番号は継続するとして)
すると x を固定した場合の、各 i に対する判定問題は 「i を Bx[i] に置き換える操作を K 回行って、 最終的な i が1周以内(=i+N 以下)に収まっていればよい」ということになる。
これはダブリングで高速化でき、全ての i=1~N に対して O(NlogK) で求められる。
実装上のテクニックとして、OutOfIndex にならないよう Bx は2周分+さらに末尾に 2N+1 を追加しておき、
遷移後の値が 2N+1 を超える場合は 2N+1 で上限を切るとよい。
ダブリング中、2N+1 を超えそうになったら 2N+1 でループするようになる。
1周以内に収まっているかの判定の最大値が i=N から始まったときの「2N 以下か?」であり、
2N+1 まで行ったらどの i に対してもNGであることが確定するので、それ以上の具体的な値の情報は必要ない。
判定問題を満たす i が1つでもある最大の x が、答えの1つめ。
改めてダブリングによる K 回遷移後の i を求め、 1周以内に収まっていない i の個数が答えの2つめ「そこで切ったら最大値を達成できない切り目」の個数となる。
「乗法的関数のPrefix-Sum」という手法を用いる。知らないと無理。知ってても本番中に考察して応用して実装するのは厳しい。
x=pe11pe22... と素因数分解できるとき、 約数の総和は (1+p1+p21+...+pe11)(1+p2+p22+...+pe22)... で求められる。
この (1+p+p2+...+pe) というパートは、
となり、少なくともどれか1つの素因数とその指数 (pi,ei) で (1+pi+p2i+...+peii) が3の倍数となってくれれば、x は「良い整数」となる。
この判定法を用いて、x=1~N に対し x が良い整数である時のみ以下の問題を解くと、その総和が答えとなる。
これは x の素因数毎に独立に考えることができて、例えば x=360=233251 を例に取ると、
これらの積で、3HM×2HM×1HM で求められる。
だが、これは当然、x を最大 1010 個 調べないといけないので、TLE。
正整数を引数に取る関数 f(x) が次の条件を満たすとき、f(x) は乗法的関数(Multiplicative Function)であるという。
上記の通り、「g(x):= 総積が x となるような、長さ M の数列の個数」は、乗法的関数である。
このような乗法的関数の1から N までの総和 F(N)=N∑x=1g(x) は、 後述するが高速に計算する手法がある。
今回は「良い整数」のみについて g(x) を足し合わせないといけないので そのままは適用できないが、工夫すれば適用できるようになる。
まずは、良い整数の要素は除いて、F(N) を求めることを考える。
特殊な N 頂点の木でDPをするアルゴリズム。
N=25 ① ,-----------------,-'`----,--,--,--,--,--, ② ③ ⑤ ⑦ ⑪ ⑬ ⑲ ㉓ ,--'`--,--,--,--, /`,--, | ④ ⑥ ⑩ ⑭ ㉒ ⑨ ⑮ ㉑ ㉕ /`,--, | ⑧ ⑫ ⑳ ⑱ /\ ⑯㉔
以下、Lpf(x):=x の最大の素因数 とする。
親から子への視点で整理すると、 頂点 v は「Lpf(v)~⌊Nv⌋」の間の素数を v にそれぞれかけた番号の子を持つ。
仮に(N が大きいので実際には無理だが)N 以下の全ての素数 p=2,3,5,... につき g(p) が計算済みであり、 累積和を取ることで、任意の区間の「素数のみの g(p) の和」が O(1) で求められるとする。
すると、乗法的関数の性質「互いに素なら、g(ab)=g(a)g(b)」を利用すると、 たとえば②の子④⑥⑩⑭㉒(それぞれ2に2,3,5,7,11がかけられた数)のうち、
これは、v= ③や⑧など、他の内部ノード(葉以外の頂点)でも G を使って同様のことが言え、
という処理を全ての内部ノードについて行うことで、x=1~N の全ての g(x) の総和が求められることになる。
葉ノードは個別処理する必要が無い、というのがこのアルゴリズムの肝となる。
で、実際にこの木において内部ノードの数は少なく、N=1010 に対して 6.3×106 程度で済むらしい。
これで、「G:ある区間の素数だけの g(x) の和」が求められれば、現実的な時間で F(N) を求められることがわかった。
肝心の G をどうやって求めるか。
N≤1010 のような制約で、N 以下の全ての素数に対して g(p) なんて求められない。
よく考えると、Black Algorithm の過程で実際に必要とされる G(l,r) の l,r の値って、実はそんなに多くない。
内部ノード x の処理を考えるとき、必要となる l,r は
l の値の範囲は r に内包されるので、結局、Q(N) に含まれる各数 qi について Fprime(qi) が求められれば、 Black Algorithm の過程で必要な G(l,r) に対して全て答えられるということになる。
で、乗法的関数においてこれをするのがLucy DPとなる。
今回は、良い整数だけを数えなければならない。
Black Algorithm で x の子の g(∗) をまとめて足し上げる時に、
よって、G に加えて「H(l,r):=l より大きく r 以下の、3で割って2余る素数のみの g(p) の総和」があれば、 良い整数に限定した数え上げも可能となる。
H は、Lucy DPで「素数全体」に加え 「3で割って1余る素数の個数」「3で割って2余る素数の個数」に分けて管理していくと、上手く計算できる。
Black Algorithm 上でDFSをするにあたり、
などを持ちながらおこなうとよい。