Atcoder-ABC200をpythonで問題を解いてみた
目的
- Atcoderで上を目指すべく頑張っている中でABC200を解いたので、解説してみる
- pypy7.3を使って解いている
A問題
考察
- Nが100で割り切れる場合とそうでない場合がポイントになりそう
- 100で割り切れる場合は、そのまま出力する
- そうでない場合は、100で切り捨て除算した値に1を足す
- ex. N=2021 の場合、20になるが、実際は21世紀なので1を足す
実装
N = int(input()) if N % 100 == 0: print(N//100) else: print(N//100+1)
結果
B問題
考察
- K<=20 なので問題文通りに実装し、全探索すれば良さそう
- pythonの場合、文字列200を後ろに付け加える部分は、一時的にstrで計算したのちに、intに直せばよさそう
実装
N, K = map(int, input().split()) for _ in range(K): if N % 200 == 0: N = N//200 else: N = int(str(N) + "200") print(N)
結果
C問題
考察
- N<=2*105 なので、全ての組みを探索するとTLE(実行時間超え)になりそう
- i, j に対して2重ループ書くことになり、計算量が 1010 程度になるため
- 入力例を見て行ったときに、200で割った余りが同じもの同士を用いて、組み合わせを用いることで、数え上げられそうと気づく
- ex1. 123, 223, 123, 523, 200, 2000
- 200で割った余りはそれぞれ、 123, 23, 123, 123, 0, 0
- 同じ余りのグループを作ると、(1, 3, 4) (5, 6) という集合ができる
- 上記グループよりそれぞれ2つ取り出す組み合わせが答えになる
- (1 ,3), (1, 4), (3, 4), (5, 6) が答えになる
- 今回は具体の組み合わせは不要で、何通りあるか出せば良いので 3C2+2C2 = 4 となる
- ex1. 123, 223, 123, 523, 200, 2000
- 200の余りが0~199になるため、配列を用意し、与えられる数列Aの要素の余りを計算し、特定の余りを持つ要素の数を数え上げ、組み合わせを用いてそれぞれ足し合わせていけばよさそう
実装
import math N = int(input()) A = list(map(int, input().split())) q = [0] * 200 ans = 0 def comb(n, r): return math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) for a in A: idx = a % 200 q[idx] += 1 for i in q: if i >= 2: ans += comb(i, 2) print(ans)
結果
D問題
方針を誤り、時間内に解くことができなかった
考察
N<=200 のため、全ての場合の和を数え上げることも、bit探索で数え上げることも、時間内には不可能
- そのため、最初は累積和を求めて、累積和同士の差分を求めることで数え上げようとした
- この場合 20000回程度の計算量に収まり、解くことができると考えた
- ただ、この場合は連続した数列部分のみを探索することになり、例えば 1, 2, 5 と並ぶような数列はこのパターンから外れるため、答えとして No が必要以上に出てしまうことになる
- ここから方針転換し、どうやって解こうと思って考えてて時間切れしてしまった・・・
- そのため、最初は累積和を求めて、累積和同士の差分を求めることで数え上げようとした
今回は「鳩の巣原理」を用いて考えればよかったそう(解説を見て理解したw)
鳩ノ巣原理: n, mを自然数 n > mとする n個のものをm組にわけるとき、少なくともひとつの組は2個以上のものを含む
今回の問題に当てはめて利用する場合は、「200で割った余りは、200通りなので、201通り以上の範囲を調べれば少なくとも1つの組は2個以上含むことになり、B, Cが見つかることになる。」となる
- 28=256 なので、bit探索で与えられた数列の頭8番目までの要素を全探索することで1組見つけることができる
- 最悪のケースは頭8個の数列の部分数列の総和は、全て違う値になることだが、201通り以上探せば同じものは見つかる
- 数列内に同じ数字がある場合は、総和が同じになり普通に同じ余りになる数列が見つかる
- 28=256 なので、bit探索で与えられた数列の頭8番目までの要素を全探索することで1組見つけることができる
存在する場合はひとつ出力してください
= 「少なくとも1つ以上存在する」のため、全部を丁寧に探す必要はないと言うヒントのため、見逃さないようにしたい- 1つでも見つかればいいので、探索する範囲を適切に制限することで解ける!場合が多そう(もちろん、効率よく全探索する場合もあるのだろうが)
実装
N = int(input()) n = min(N, 8) A = list(map(int, input().split()))[0:n] q_list = [[] for _ in range(200)] def solve(): for i in range(1, 2 ** n): bit = bin(i)[2:].rjust(n, "0") s, l = 0, [] for i, b in enumerate(list(bit)): if b != "1": continue s += A[i] l.append(i+1) idx = s % 200 q_list[idx].append(l) if len(q_list[idx]) >= 2: return idx return None ans = solve() if ans is None: print("No") else: print("Yes") x, y = q_list[ans][1], q_list[ans][0] print(*([len(x)]+x)) print(*([len(y)]+y))
結果
振り返り
- A, B ,C をもっと安定し解けるようになり、かつ20分以内に解けるようになりたい
- 今回のD問題のように、「あとちょっとでAC」になった際にDebugしてしっかり解き切れるようになるための力をつけたい
- 下記とかが参考になりそう
- E問題はDPで、DP勉強してといてみるのもアリだが、当分はA, B, Cの早解をしてrateをあげるのが良さそう
- 基本アルゴリズムの勉強は茶色になってから考える