AtCoder Beginner Contest 125 C,D 問題メモ
Rating対象が1199までとなる最後の(?)ABCでした。次からは1999まで対象。ふるえてねむれ。
C - GCD on Blackboard
問題
- 黒板に NN 個の整数 A1,A2,...,ANA1,A2,...,AN
- 1つだけ好きな数字に書き換えられる
- 書き換える数字を上手く選んだとき、書き換えた後の NN 個の整数の最大公約数の最大値を求めよ
- 2≤N≤1052≤N≤105
- 1≤Ai≤1091≤Ai≤109
解法
数字を1つ選んだら他の N−1N−1 個の数字は変更しないので、答えはその N−1N−1 個の最大公約数 GG より大きくはならない。
よって変更する数字も GG(の倍数)にすればよいので、言い換えると「数字を1つ除いて最大公約数を最大化せよ」という問題である。
1つ1つ数字を除いてみて N−1N−1 個の最大公約数を求めていると間に合わない。
2つの配列を使って事前計算し、共通の計算を省略する。
- f[i]=A1~Ai(先頭 i 個)の整数の最大公約数
- b[i]=Ai~AN(末尾 N−i+1 個)の整数の最大公約数
A 12 24 18 27 36 --------------------- f 12 12 6 3 3 b 3 3 9 9 36
そうしたら、Ai を除いた場合の最大公約数とは、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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from fractions import gcdn = 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
問題
- N 個の整数 A1,A2,...,AN が並ぶ
- この整数列に対して次の操作を好きなだけ行える
- 隣り合う2数を選び、-1 を乗算する
- 操作終了後の整数列を B1,B2,...,BN とする
- 上手く操作を行ったときの、B1+B2+...+BN の最大値を求めよ
解法
体感的にはCより簡単なような。直感的に操作の推移がイメージしやすいからか。
- 正の数字をなるべく多くしたい
- 同じ数字に対し2回操作を行ったら元に戻るので、2回以上同じ2数で操作を行う意味は無い
2つ並んだ負の数があれば、操作を行うことで両方正にできる。
3 -5 -7 9
↓
3 5 7 9
1つだけの負の数があれば、操作を行うことでその数は正になり、左隣か右隣が負になる(0という可能性もあるが)。 「負の符号の位置が移動した」と捉えると、移動先の数に対しても繰り返し操作を行うことで、好きなように移動させることができる。
3 -5 7 9
↓
3 5 -7 9
↓
3 5 7 -9
なので、負を全部、たとえば右端に追いやってしまって、2個隣り合ったら消すという操作方針にすると、
- 初期状態で負の数が偶数個なら、全部正にできる
- 初期状態で負の数が奇数個なら、1個を除き正にできる。負の1個は好きな数字に移動できる
初期状態の負の数を数えて、偶数個なら絶対値を取って合計すればよい。奇数個なら、絶対値が最小の数のみを負にすればよい。
1 2 3 4 5 6 7 8 |
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) * 2print(ans) |

