目次

AtCoder Beginner Contest 125 C,D 問題メモ

AtCoder Beginner Contest 125

Rating対象が1199までとなる最後の(?)ABCでした。次からは1999まで対象。ふるえてねむれ。

C - GCD on Blackboard

C - GCD on Blackboard

問題

解法

数字を1つ選んだら他の $N-1$ 個の数字は変更しないので、答えはその $N-1$ 個の最大公約数 $G$ より大きくはならない。

よって変更する数字も $G$(の倍数)にすればよいので、言い換えると「数字を1つ除いて最大公約数を最大化せよ」という問題である。

1つ1つ数字を除いてみて $N-1$ 個の最大公約数を求めていると間に合わない。

2つの配列を使って事前計算し、共通の計算を省略する。

A  12  24  18  27  36
---------------------
f  12  12   6   3   3
b   3   3   9   9  36

そうしたら、$A_i$ を除いた場合の最大公約数とは、$f[i-1]$ と $b[i+1]$ の最大公約数となる。

A3 = 18 を除いた場合の最大公約数
f  12  ⑫   6   3   3
b   3   3   9  ⑨  36  →  12 と 9 の最大公約数 = 3

A4 = 27 を除いた場合の最大公約数
f  12  12  ⑥   3   3
b   3   3   9   9  ㊱  →  36 と 6 の最大公約数 = 6

一番端を除く場合だけ、ちょっと注意。

A1 = 12 を除いた場合の最大公約数
f  12  12   6   3   3
b   3  ③   9   9  36  →  3
A5 = 36 を除いた場合の最大公約数
f  12  12   6  ③   3  →  3
b   3   3   9   9  36

この中の最大値を求めればよい。

Pythonの標準ライブラリにある fractions.gcd(a,b) は、片方が0の場合、もう片方の数をそのまま返す。

よって、端の数字を除く場合の処理のため便宜的に0を仕込み、indexをずらしておくと、簡潔に記述できる。

f   0  12  12   6   3
b   3   9   9  36   0
---------------------
gcd 3   3   3   6   3

from fractions import gcd

n = int(input())
aaa = list(map(int, input().split()))
p = aaa[0]
f = [0, p]
for a in aaa[1:-1]:
    p = gcd(p, a)
    f.append(p)
aaa.reverse()
p = aaa[0]
b = [0, p]
for a in aaa[1:-1]:
    p = gcd(p, a)
    b.append(p)
b.reverse()

print(max(gcd(p, q) for p, q in zip(f, b)))

D - Flipping Signs

D - Flipping Signs

問題

解法

体感的にはCより簡単なような。直感的に操作の推移がイメージしやすいからか。

2つ並んだ負の数があれば、操作を行うことで両方正にできる。

3  -5  -7   9
     ↓
3   5   7   9

1つだけの負の数があれば、操作を行うことでその数は正になり、左隣か右隣が負になる(0という可能性もあるが)。 「負の符号の位置が移動した」と捉えると、移動先の数に対しても繰り返し操作を行うことで、好きなように移動させることができる。

3  -5   7   9
     ↓
3   5  -7   9
         ↓
3   5   7  -9

なので、負を全部、たとえば右端に追いやってしまって、2個隣り合ったら消すという操作方針にすると、

初期状態の負の数を数えて、偶数個なら絶対値を取って合計すればよい。奇数個なら、絶対値が最小の数のみを負にすればよい。

n = int(input())
aaa = list(map(int, input().split()))
bbb = list(map(abs, aaa))
if sum(1 for a in aaa if a < 0) % 2 == 0:
    ans = sum(bbb)
else:
    ans = sum(bbb) - min(bbb) * 2
print(ans)