くろたんく雑記帳

日常とか、わんちゃんとか、機械学習とか、競プロとか、

MENU

Python3で解く AtCoder Beginner Contest 150 D - Semi Common Multiple

半公倍数って初めて聞いた。 まぁ定義に従って計算する。
ぱっと見、複数の値に対する最小公倍数を使うんだろうな。

結果的に、最小公倍数を計算しきってから大きさの判定をしていたせいで、一部REが発生していて、そこが原因と気付かず、ひたすら原因究明のためにsubしまくって、ようやく正解。

この順番じゃないとダメな理由は、よくわからなかったけど、多分オーバーフローとかそういうことでしょう。

概要

問題

  • 偶数個の数列{\displaystyle a}を与えられる。
  • その数列の任意の{\displaystyle a_k}において、
    {\displaystyle X = a_k \times (p + 0.5)}を満たす{\displaystyle p}が存在する時、{\displaystyle X}を半公倍数とする。
  • 半公倍数の数は?

解くときに考えた内容

  1. まず複数の最小公倍数({\displaystyle a, b}の最小公倍数を{\displaystyle lcm(a, b)}とする)を求める。
    • 最小公倍数をとった値に逐次的に最小公倍数をとっていけばいい。
    • つまり、{\displaystyle a, b, c}があるとして、{\displaystyle lcm(lcm(a, b), c)}とすればいい。
  2. 最小公倍数の半分の値({\displaystyle h}とする)が{\displaystyle m}以下かどうか
    これは{\displaystyle a_k \times (p+0.5)}となるようなpが存在するかということで、最も小さい条件を満たすものは最小公倍数の半分の値であり、それが条件を満たさない(つまり選んだ全ての{\displaystyle a_k}において{\displaystyle p_k+0.5}の形にならなずに、整数になってしまうものが存在する)場合は、半公倍数は存在しない。
  3. あとは{\displaystyle p}が負ではないということになっているので、{\displaystyle h + k \times lcm(A) \leqq m} までの{\displaystyle k}を考えればいい。 {\displaystyle k=0, 1, 2, 3 ...} これは、上で最小公倍数の半分の値で成立すれば、あとはそれに最小公倍数を足していけば必ず成立する(必ず{\displaystyle (p+0.5)}に整数が足されていくため)。最小公倍数以外を足したら任意の{\displaystyle a_k}で成立しないからである。
    f:id:black_tank_top:20200717083240j:plain
    最小公倍数の半分の値で成立したら、その後最小公倍数を足していくと成立するイメージ
  4. {\displaystyle k}として取り得る最大を{\displaystyle k_{max}}とする。
  5. {\displaystyle 1 + k_{max}}が答え

コード

lcm計算中に{\displaystyle m}との大きさの判定をする方法
def gcd(a, b):
    if b == 0:
        return a
    else:
        return gcd(b, a%b)

def lcm(a, b):
    return (a // gcd(a, b)) * b

n, m = map(int, input().split())
A = set([int(x) for x in input().split()])
a_lcm = 1
for a in A:
    a_lcm = lcm(a_lcm, a)
    # mがlcmの半分よりも小さかったら0
    if (a_lcm / 2) > m:
        print(0)
        exit()
for a in A:
    if a_lcm / 2 % a == 0:
        print(0)
        exit()
# # あとは定数倍の数調べて終わり
ans = int((m - (a_lcm / 2)) // a_lcm) + 1
print(ans)
lcm計算後に{\displaystyle m}との大きさの判定をする方法(これはREが出る)

これで死ぬほどハマった。

def gcd(a, b):
    if b == 0:
        return a
    else:
        return gcd(b, a%b)

def lcm(a, b):
    return (a // gcd(a, b)) * b

n, m = map(int, input().split())
A = set([int(x) for x in input().split()])
a_lcm = 1
for a in A:
    a_lcm = lcm(a_lcm, a)
# mがlcmの半分よりも小さかったら0
if (a_lcm / 2) > m:
    print(0)
    exit()
for a in A:
    if a_lcm / 2 % a == 0:
        print(0)
        exit()
# # あとは定数倍の数調べて終わり
ans = int((m - (a_lcm / 2)) // a_lcm) + 1
print(ans)