AtCoder Beginner Contest 125 C,D 問題メモ
Rating対象が1199までとなる最後の(?)ABCでした。次からは1999まで対象。ふるえてねむれ。
C - GCD on Blackboard
問題
- 黒板に N 個の整数 A1,A2,...,AN
- 1つだけ好きな数字に書き換えられる
- 書き換える数字を上手く選んだとき、書き換えた後の N 個の整数の最大公約数の最大値を求めよ
- 2≤N≤105
- 1≤Ai≤109
解法
数字を1つ選んだら他の N−1 個の数字は変更しないので、答えはその N−1 個の最大公約数 G より大きくはならない。
よって変更する数字も G(の倍数)にすればよいので、言い換えると「数字を1つ除いて最大公約数を最大化せよ」という問題である。
1つ1つ数字を除いてみて N−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 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
問題
- 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) * 2 print (ans) |