ACL Contest 1 B - Sum is Multiple

あらすじ

atcoder.jp

解けて嬉しかったのと、解説と少し違ったのでメモです。

問題概要

整数Nが与えられる。1 + 2 + ... + k Nの倍数になる最小のkを求める。
 1 \le N\le 10^{15}

考察の流れ

はじめに

N = 1の場合、明らかに答えは1です。
天下り的で申し訳ないのですが後々面倒なので、以降N ≧ 2として考えます。

まず

 
\sum_{i=1}^k = N \\\\
k(k+1) / 2 = N \\\\
k(k+1) = 2N

まず(整数の積) = 2N となっていることから、2Nを素因数分解するとよさそうです。

素因数の分割

とりあえず具体例を挙げながら考えます。
ひとまず、2N = 23 * 32 * 52 あたりにします。(N=900)
kの素因数とk+1の素因数は合わせて最低でも2を3個、3を2個、5を2個持つ必要があります。

ここで重要な性質としてkとk+1は差が1なので互いに素です。
共通の因数を持たないことから例えば2を両方に割り振ることができず、23を片方が持つ必要があります。

これを考えると(それぞれが少なくとも持つ)因数の割り振り方は

 1 vs 2^3*3^2*5^2,  2^3 vs 3^2*5^2 , ....  2^3*3^2*5^2 vs 1 

で2 ^ (2Nの素因数の数) 通りになります。これらの分け方を総当たりします。

各分割の中での考察

前節で考えた割り振りの各パターンについて考えます。それぞれに割り振った素因数の積をx, y と置きます。 (例: x = 23*52, y = 32)
ax - by = 1 となるような自然数a, bでbyが最小になるようなものを見つける必要があります。

x と y は最大公約数が1のため、 ax + by = 1となるような整数の組 (a, b) は拡張ユークリッドの互除法で求めることができます。
以下、得られたxとy の符号で場合分けをします。

  • (a > 0 かつ b > 0) もしくは (a < 0 かつ b < 0)
    x ≧ 1, y ≧ 1 なので出現しません。 ax + by の値が 1より大きくなるか、負になるため
  • a > 0 かつ b < 0
    求めていた条件に一致します。-byがこの分割で得られる k として最適です。 条件を満たすa, b 自体は任意の自然数tに対して (a', b') = (a+ty, b-tx) でいくらでも得ることができますが、この解から得られるk' は k < k' のため最適ではありません。
  • a < 0 かつ b > 0
    (a', b') = (a+y, b-x) で a > 0 かつ b < 0 のパターンに持っていけます。(ここ少し不安なので、間違っていたらすみません...)
  • a = 0 または b = 0
    コーナーケースで、注意する必要があります。
    x, y の分割が 1と2Nになった時に、1 * 1 + 0 * 2N = 1という解が得られますが、ここから a > 0 かつ b < 0のパターンと同じようにk = 0と持っていくのは明らかに誤りです。
    分割が1と2Nの場合、k = 2N-1, k+1 = 2N とするのが最適です。 (片方が因数に2Nを持つ、差が1、積が2Nの倍数の中で最小のペア)

以上のいずれかで、分割を決めた中で最適のkを得ることができるので、これを他の分割から得られたkと比較して最小のものを出力します。

計算量

  •  N素因数分解 O(sqet(N))
  • 得られた  p 個の数 [(素因数1)^{x_1}, (素因数2)^{x_2}, ... , (素因数p)^{x_p} ] を2つに分割する選び方は2^{p}通り
  • それぞれの分割で拡張ユークリッドの互除法を行うのにO(log(N))

で、 O(sqrt(N) + 2^{p}log(N))で解けるはずです。

pの雑な見積もり方ですが、素因数をp個持つ最小の自然数は、素数のうち小さい方からp個の積です。 p = 15程度でこの数は1015を十分に超えるので、p≦15として制限時間に間に合います。

終わりに

今回は色々回り道を試して答えにたどり着くことができましたが公式解説がかなり綺麗だったので、知識を付けてその力で問題を簡潔にするという方法もできるようになっていきたいなと思いました。 拡張ユークリッドの互除法に苦手意識がありましたが、けんちょんさんの記事

拡張ユークリッドの互除法 〜 一次不定方程式 ax + by = c の解き方 〜 - Qiita
の力を借りて解くことができたので苦手意識が減ったのは嬉しいですね。

間違いなどありましたらコメントもしくは @sentya7までお知らせください。 お読みいただきありがとうございました。