ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

ABC020 D - LCM Rush

問題
提出コード(100点)
提出コード(満点)
100点から満点にもっていくことができず、解説を見ました…
言われれば確かにそうだけど~って感じですね。

解法

まずは100点までです。
Kの制約が少ないので、Kについて何かをするんだろうな、という気持ちになります。
すると、Nまでの数の中を、Kで割った余りによって分類すると、
そのあまりが等しい数については、最大公約数も等しいことがわかります。
ここで、xyの最小公倍数LCM(x,y)は、おなじくxyの最大公約数をGCD(x,y)と表現したとき、
\frac{xy}{GCD(x,y)}
と表すことができます。
ので、Kで割った余りがxとなるような数の総和をもとめ、そこにK/GCD(x,K)をかけてあげれば、Kで割った余りがxとなるような数と、Kの最小公倍数の総和が求められます。
これを、全てのxに対して行えば、O(K)でこの問題を解くことができます。
Kで割った余りがxとなるような数は、
iK + xと表記することができます。
ここで、i0以上(n-x)/K (切り捨て)以下の数となります。
p = (n-x)/Kとおくと、
Kで割った余りがxとなるような数の総和は、次のような計算式で求められます。
 px + \frac{Kp(p+1)}{2}
ということで、
 (px + \frac{Kp(p+1)}{2})  \times \frac{K}{GCD(x,K)}
1~Kxに対して行えば、答えを求めることができます。10^{9}+7で余りをとるタイミングや、割り算のタイミングに気を付けましょう。
ということで、ここまでは今回自分が自力でできた部分です。
ここから先は、素因数分解やらなんやらをするのかなぁと思っていたのですが…
解法自体は解説そのままです。
結局、GCD(i,K)のパターンは、Kの約数にしかならないので、1~Nxについて、GCD(x,K)のパターンによって分けて総和を求め、あとは上と同じようにその総和と\frac{K}{GCD(K,x)}の積を求めていけば答えが求まる、というものです。
そのために、まずはGCD(K,x) = gとなるようなxの総和を求める方法を考えてみます。
これは、GCD(K/g,x/g) = 1となるようなx/gの総和を求め、最後にg倍したものと同じになります。 この総和は、包除原理を用いると、うまく計算することができます。
K/g素因数分解し、素因数の種類を洗い出します。今回は、例として3種類の素因数が存在し、a^2 b^3 c^4 = K/gだったとします。
このとき、まずは1 ~ x/gについての総和を求め、そこからGCD(K/g,i) \neq 1となるようなiを引いていくことを考えます。
そして、この後は

  • 素因数を奇数種類使用した数の倍数の総和を減算し、偶数種類使用した数の倍数の総和を加算する

という操作を行います。
今回の例だと、a,b,cおよびabcそれぞれについて、1 ~ x/gの中での倍数の総和を求め、減算し、ab,bc,acの倍数の総和については加算し直します。また、同じ素因数が2つ以上含まれているような場合(a^2bcなど)は、無視します。
総和を求めるときは、上で行ったような総和の求め方と同じようなことをします。
aの倍数は、p/a個(切り捨て)存在するので、これをj個としたときに、
\frac{aj(j+1)}{2}
という計算をすれば、総和を求めることができます。
また、素因数の種類および、同じ種類の素因数が2つ以上使用されているかどうかは、よくある素因数分解アルゴリズムを用いれば、容易に調べることができます。


さて、GCD(K/g,x/g) = 1となるようなx/gの総和を求めることができたので、あとはg倍すればGCD(K,x) = gとなるようなxの総和になります。
あとは、全てのgについて同じことを繰り返せば終わりです。gは、先ほどと同様よくある素因数分解等のアルゴリズムのようなものを使えば、O(\sqrt{K})で洗い出すことができます。
ということで、これらの式をバグなく気合で組み立て計算すれば、この問題を解くことができます。