ABC125 C - GCD on Blackboard

備忘録

問題

atcoder.jp

回答

import sys
import os
import math
import bisect
import itertools
import collections
import heapq
import queue
import array

# 時々使う
# from scipy.sparse.csgraph import csgraph_from_dense, floyd_warshall
# from decimal import Decimal
# from collections import defaultdict, deque

# 再帰の制限設定
sys.setrecursionlimit(10000000)


def ii(): return int(sys.stdin.buffer.readline().rstrip())
def il(): return list(map(int, sys.stdin.buffer.readline().split()))
def fl(): return list(map(float, sys.stdin.buffer.readline().split()))
def iln(n): return [int(sys.stdin.buffer.readline().rstrip())
                    for _ in range(n)]


def iss(): return sys.stdin.buffer.readline().decode().rstrip()
def sl(): return list(map(str, sys.stdin.buffer.readline().decode().split()))
def isn(n): return [sys.stdin.buffer.readline().decode().rstrip()
                    for _ in range(n)]


def lcm(x, y): return (x * y) // math.gcd(x, y)


# MOD = 10 ** 9 + 7
MOD = 998244353
INF = float('inf')


def main():
    if os.getenv("LOCAL"):
        sys.stdin = open("input.txt", "r")

    N = ii()
    A = il()
    left = [0] * (N + 1)
    right = [0] * (N + 1)

    # 予めGCDを計算する
    for i in range(N):
        # 左から累積したGCD
        left[i+1] = math.gcd(left[i], A[i])
    for i in reversed(range(N)):
        # 右から累積したGCD
        right[i] = math.gcd(right[i+1], A[i])

    ret = 0
    for i in range(N):
        ret = max(ret, math.gcd(left[i], right[i+1]))
    print(ret)

    
if __name__ == '__main__':
    main()

考え方

予め、左と右から累積したGCDを計算しておく。
累積したGCDからある整数を1つ省いた場合のGCDを求める。


まず、整数を書き換えることについて考える。
ある整数1つをどのような値にも書き換えることが可能なとき、
選んだ整数以外の整数で求められたGCDと同じ値にすることで、
GCDを最大にすることができる。

つまり、ある整数を書き換えた後の値は重要ではなく、
どの整数を選択するかだけを考えればよい。
選択した整数は省くことが出来ると考え、
選択した整数以外のGCDを求めるように考える。

ある1つの整数を省いたGCDを求めるために、
左から累積させたGCDと、右から累積させたGCDを利用する。

複数の値のGCDを求めるとき、
どのような順番で求めても答えが一意になる。
そのため、左から累積させたGCDと右から累積させたGCDを利用することで、
整数列からある1つの値を省いたときのGCDを求めることが出来る。

# 与えられる整数が[12, 15, 18]のとき、
left = [0, 12, 3, 3] # 左から累積させたGCD
right = [3, 3, 18, 0] # 右から累積させたGCD
# となる。

# 累積させたGCDから順(0 <= i < N)にGCDを求めることで
# ある整数を省いた場合のGCDを求めることが出来る
for i in range(N):
    math.gcd(left[i], right[i+1])
    # math.gcd(0, 3) -> math.gcd(0, math.gcd(15, 18)) 12を考慮しない場合
    # math.gcd(12, 18) -> math.gcd(12, math.gcd(18, 0)) 15を考慮しない場合
    # math.gcd(3, 0) -> math.gcd(math.gcd(12, 15), 0) 18を考慮しない場合

よくわかる解説
AtCoder ABC 125 C - GCD on Blackboard (300 点) - けんちょんの競プロ精進記録