ABC150 D - Semi Common Multiple
問題原文
問題要旨
長さ の 偶数 からなる正の整数列 と、 正の整数 が与えられる。
の全ての要素 に対して、以下の条件を満たす正の整数 を の「半公倍数」と呼ぶことにする。
- を満たす 負でない整数 が存在する。
以上 以下の整数のうち、 の半公倍数となるような数はいくつあるか?
解法
簡単な問題を考える
半公倍数は考えにくい。 問題を簡単にして、求めたいものが公倍数の個数の場合、つまり の条件が以下の場合を考える。
- を満たす 負でない整数 が存在する。
この条件下では、 [a_k] は の約数でなければならないことがわかる。
逆に考えて、全ての に対して条件を満たすような は の LCMであることがわかる。
邪魔な0.5を消す
なんかLCMが使えそうなのがわかった。これを半公倍数の場合にしていきたいが、条件の が邪魔。なんとかして消したい。
そこで、条件式を一度展開してしまって、
を得る。さらにこれを でくくると、
が得られる。
さらに、 は偶数なので、 とすると、
が得られる。これで邪魔な が消えた!
問題文の再定義
ここまでで問題文は以下のようになった。
長さ の 偶数 からなる正の整数列 と、 正の整数 が与えられる。
ここで、 の要素を全て で割ったものを とする。
の全ての要素 に対して、以下の条件を満たす正の整数 を の「半公倍数」と呼ぶことにする。
- を満たす 負でない整数 が存在する。
以上 以下の整数のうち、 の半公倍数となるような数はいくつあるか?
LCMが使えるかもしれないよ。
なんか少し簡単になった気がする。
実験をする
- ]
を考えてみる。
まず の要素すべてを 2で割って、 ] を得る。
次に、 の式に当てはめて、
を満たすような、負でない整数 、整数 が存在するかを考える。
少し考えると、そんなものは存在しないことがわかる。(, と、 みたいになる。)
その理由を考えると、 の部分は、 と奇数しか取れないので、 各 が持つ素因数としての2の数が同じでないとダメなことがわかる。(2以外の素数は必ず奇数で、奇数をいくらかけても奇数。奇数なら、 の部分でいかようにも)
LCMがとてつもなく大きくなることがある
答えが0になるパターンもわかったので、あとはLCMを使えばいい。でも、LCMがすごく大きくなるケースもある。 でもよくよく考えると、 を超えるようなLCMの場合には意味がないことがわかる。
まとめ
- の要素は全て2で割っておく(これを とする)
- の各要素について、 「2で割り切れる回数」が等しくないなら答えは0。
- そうでないなら、 各 についてLCMを求めて、その奇数倍が答えになる。( より)
実装
from math import ceil N, M = map(int, input().split()) A = list(map(int, input().split())) # 2で割ったものに置き換え A = [a // 2 for a in A] # 各要素について、2で割り切れる回数が等しいかチェック count_div_2 = None for a in A: cnt = 0 while a % 2 == 0: a //= 2 cnt += 1 if count_div_2 is None: count_div_2 = cnt elif cnt != count_div_2: print(0) exit() # LCM計算 while len(A) > 1: a, b = A.pop(), A.pop() my_lcm = lcm(a, b) # Mを超えたlcmは意味がない if my_lcm > M: print(0) exit() A.append(my_lcm) print(ceil(M // A[0] / 2))
感想
むずい!!!!!!!!!!!!wwwww