振り返りです。

AtCoder Beginner Contest 294 - AtCoder
AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.
import sys from io import StringIO input = lambda: sys.stdin.readline()[:-1] def test(f, data): stdin = sys.stdin stdout = sys.stdout sys.stdin = StringIO(data["in"]) out = StringIO() sys.stdout = out err = None try: f() except BaseException as e: err = e else: ans = out.getvalue() exp = data["out"] sys.stdin = stdin sys.stdout = stdout if err: raise err result = "AC" if exp == ans else "WA" print(f"{result}: expected: {exp}, output: {ans}") def test_all(f): for i, data in enumerate(eval(f'test_data{f.__name__[0]}'), 1): print(f"case {i}") test(f, data)

A - Filter

提出:#39843461

B - ASCII Art

やってみよう!

B関数を完成させて「Run」ボタンをクリックしよう!

test_dataB = [ { "in":"""2 3 0 1 2 0 0 3 """, "out": """.AB ..C """ },{ "in":"""3 3 24 0 0 0 25 0 0 0 26 """, "out": """X.. .Y. ..Z """ },{ "in":"""3 1 2 9 4 """, "out": """B I D """ },{ "in":"""24 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 13 14 0 0 0 10 0 0 0 0 0 15 24 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 23 7 25 24 13 10 0 10 12 0 0 0 0 19 9 23 0 0 0 0 10 10 14 0 0 0 10 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 13 5 0 0 23 11 14 14 0 0 12 9 1 21 19 0 0 9 12 10 25 3 10 6 0 0 9 13 23 24 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 14 6 0 0 0 10 5 25 13 0 0 25 0 0 0 0 0 0 0 0 0 0 10 16 0 0 13 21 13 13 14 23 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 8 2 0 0 0 0 0 13 11 13 19 0 0 1 2 5 9 12 12 5 9 9 20 6 0 14 14 14 9 0 0 0 14 14 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 23 13 13 13 13 13 13 14 14 14 13 14 14 13 7 0 0 0 0 0 0 0 0 0 0 0 0 13 13 13 2 0 0 0 0 13 11 13 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 1 0 0 0 9 20 9 20 20 20 20 13 20 20 13 20 23 8 8 8 20 8 20 7 8 17 7 10 13 14 13 19 0 0 0 0 0 22 14 25 13 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 20 20 13 13 7 20 26 13 8 6 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 1 2 20 20 23 13 2 7 2 10 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 20 23 13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 1 0 0 0 13 12 9 14 13 13 9 9 20 12 0 0 0 0 0 0 0 0 0 0 0 1 9 9 9 9 12 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 19 0 0 14 13 14 14 13 13 0 0 9 5 16 0 0 0 0 0 0 5 20 20 13 2 2 20 9 13 14 14 20 12 12 0 0 0 0 9 13 0 0 0 0 0 0 0 0 1 9 0 0 0 0 0 0 0 0 0 0 0 13 10 13 13 13 13 2 5 12 10 5 0 0 0 0 0 0 0 0 20 16 0 0 0 13 14 13 13 13 13 0 0 10 8 0 0 0 0 0 20 7 0 0 0 0 0 0 4 2 0 0 0 0 0 0 0 0 0 0 0 0 9 7 14 10 10 14 13 5 0 0 0 0 0 0 0 0 0 0 0 23 13 12 13 13 13 13 13 9 13 0 14 4 0 0 0 0 0 0 0 9 16 0 0 0 0 0 22 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 13 13 13 2 9 14 2 20 14 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 5 13 0 0 0 2 7 13 13 13 13 13 13 13 2 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 20 20 9 0 0 0 0 0 0 0 0 0 0 0 0 0 19 0 0 0 0 0 0 0 20 13 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 14 7 2 20 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 6 0 0 0 0 0 0 0 0 0 9 14 14 0 0 20 20 13 13 20 13 9 0 0 10 0 0 0 0 0 0 9 23 13 9 0 0 0 0 0 0 0 10 6 0 0 7 0 0 9 20 13 13 14 2 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 20 13 14 0 0 0 0 0 0 0 0 13 9 0 0 0 0 0 0 0 0 13 11 0 0 0 0 0 0 0 14 9 0 0 0 20 25 14 7 0 0 0 0 9 1 14 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 13 13 20 0 0 0 0 0 9 12 0 0 0 0 0 0 0 14 13 14 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 9 9 20 14 14 4 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 2 9 0 0 0 0 9 9 20 21 7 13 20 0 0 20 23 7 7 2 12 7 6 0 0 0 0 0 0 0 0 0 0 0 0 5 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 20 9 9 20 24 10 12 10 0 0 0 0 0 0 0 0 20 20 0 0 0 0 0 20 7 7 13 22 2 5 9 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 20 5 20 5 2 5 7 20 5 14 14 5 11 5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 """, "out": """................................B........................... ...................MMN...J.....OXN.......................... .................MWGYXMJ.JL....SIW....JJN...JN.............. ...............IME..WKNN..LIAUS..ILJYCJF..IMWXN............. ..............MNF...JEYM..Y..........JP..MUMMNWN............ .............MHB.....MKMS..ABEILLEIITF.NNNI...NNR........... ..........JWMMMMMMNNNMNNMG............MMMB....MKMP.......... ........MA...ITITTTTMTTMTWHHHTHTGHQGJMNMS.....VNYMP......... ......MI...............................TTTMMGTZMHFN......... .....E.......ABTTWMBGBJL.......................TTWM......... ....M........A...MLINMMIITL...........AIIIIL.......T........ ...B..........S..NMNNMM..IEP......ETTMBBTIMNNTLL....IM...... ..AI...........MJMMMMBELJE........TP...MNMMMM..JH.....TG.... ..DB............IGNJJNME...........WMLMMMMMIM.ND.......IP... ..VM.................................TMMMBINBTN.........B... ...EM...BGMMMMMMMBI....................ITTI.............S... ....TML..................UNGBTX........................IF... ......INN..TTMMTMI..J......IWMI.......JF..G..ITMMNB....E.... ........TMN........MI........MK.......NI...TYNG....IANG..... ..........IMMT.....IL.......NMN.......F........IITNNDN...... ..............TBI....IITUGMT..TWGGBLGF............EE........ .................TTIITXJLJ........TT.....TGGMVBEIB.......... .........................ITETEBEGTENNEKEB................... ............................................................ """ }, ] def B(): pass test_all(B)

自由欄

コンテスト中

こんな感じ。

def B(): from string import ascii_uppercase abc = ["."] + list(ascii_uppercase) H, W = map(int, input().split()) A = [list(map(int, input().split())) for _ in range(H)] for i in range(H): print(*[abc[A[i][j]] for j in range(W)], sep="") test_all(B)

結果:#39851403

自由欄

振り返り

「対応付ける」と言えばmap関数でしょう。(専ら入力を受け取る時に使ってるので本来の用途を忘れがちでした…)

という訳で以下。
コンテスト中よりちょっとすっきり。

def B(): from string import ascii_uppercase as ABC ABC = "." + ABC H, _ = map(int, input().split()) A = [list(map(int, input().split())) for _ in range(H)] for a in A: print(*map(lambda x: ABC[x], a),sep="") test_all(B)

結果:#40004440

自由欄

C - Merge Sequences

やってみよう!

C関数を完成させて「Run」ボタンをクリックしよう!

test_dataC = [ { "in":"""4 3 3 14 15 92 6 53 58 """, "out": """1 3 4 7 2 5 6 """ },{ "in":"""4 4 1 2 3 4 100 200 300 400 """, "out": """1 2 3 4 5 6 7 8 """ },{ "in":"""8 12 3 4 10 15 17 18 22 30 5 7 11 13 14 16 19 21 23 24 27 28 """, "out": """1 2 5 9 11 12 15 20 3 4 6 7 8 10 13 14 16 17 18 19 """ }, ] def C(): pass test_all(C)

自由欄

コンテスト中

こんな感じで、まぁそうだろうなってコードなんですが10分以上掛かってる…
今回のコンテストではE問題まで茶Diffなのでレートアップを狙うには速解きが重要でした。
その意味ではもっとサクッと解けるべき問題だったなぁ😩

def C(): N, M = map(int, input().split()) A = [(x, "A") for x in map(int, input().split())] B = [(x, "B") for x in map(int, input().split())] *C, = enumerate(sorted(A + B), 1) print(*[i for i, (x, AorB) in C if AorB == "A"]) print(*[i for i, (x, AorB) in C if AorB == "B"]) test_all(C)

結果:#39859575

自由欄

振り返り

速解きを意識したらこうなるかな~というのが以下。

def C(): input() *A, = [(a, "A") for a in map(int, input().split())] *B, = [(b, "B") for b in map(int, input().split())] ansA = [] ansB = [] for i, (_, AorB) in enumerate(sorted(A + B), 1): if AorB == "A": ansA.append(i) else: ansB.append(i) print(*ansA) print(*ansB) test_all(C)

結果:#40005261

自由欄

D - Bank

やってみよう!

D関数を完成させて「Run」ボタンをクリックしよう!

test_dataD = [ { "in":"""4 10 1 1 3 2 1 1 2 3 3 1 2 2 3 """, "out": """1 2 4 """ } ] def D(): pass test_all(D)

自由欄

振り返り

設問の理解が不十分で無駄に複雑なコーディングをしてしまった案件です。

とりあえず以下、コンテスト中の提出がインデックスの管理が分かりづらいので微修正したバージョン。
「まだ呼ばれてない人」と「呼ばれて受付に行ってない人」を二分探索のメソッドを追加したBITで管理しています。

class BIT: def __init__(self, a): if hasattr(a, "__iter__"): self.size = len(a) self.data = [0] * self.size self.bit = [0] * (self.size + 1) for i, j in enumerate(a): self.add(i, j) elif isinstance(a, int): self.size = a self.data = [0] * a self.bit = [0] * (a + 1) else: raise TypeError() def add(self, i, x): self.data[i] += x i += 1 while i <= self.size: self.bit[i] += x i += i & -i def sum(self, i): '''Get sum of data[:i] ''' total = 0 while i > 0: total += self.bit[i] i -= i & -i return total def get(self, x): res = 0 i = 1 while i < (self.size + 1) / 2: i <<= 1 while i > 0: if res + i < self.size and self.bit[res + i] < x: x -= self.bit[res + i] res += i i >>= 1 res += 1 return res if res >= x else -1 def __getitem__(self, key): return self.data[key] def __setitem__(self, key, value): self.add(key, value - self[key]) self.data[key] = value def __len__(self): return self.size def __repr__(self): return self.data.__repr__() def __iter__(self): return self.data.__iter__() def D(): N, Q = map(int, input().split()) no_called = BIT([0] + [1] * N) called_no_gone = BIT(N + 1) for _ in range(Q): # print(no_called.data) # print(called_no_gone.data) ev, *x = input().split() if ev == "1": i = no_called.get(1) - 1 no_called[i] = 0 called_no_gone[i] = 1 elif ev == "2": called_no_gone[int(x[0])] = 0 else: print(called_no_gone.get(1) - 1) test_all(D)

結果:#40007490

自由欄

解説を見る①

上の提出でACとなったのですが、BITでの管理の考え方だったりインデックスずれのデバッグだったりで時間を費やしてしまいました。
「もっとスマートな方法ないかなぁ」と解説を見て、1つ目の衝撃が以下。

  • lastを最後に呼ばれた人の番号とする。便宜上はじめlast0とする。
  • 1 種類目のイベントの場合、次に呼ばれるのはlast + 1である。

という事で、「まだ呼ばれてない人」をBITで管理する必要ねーじゃん!!!!!!!!!!
はい、ちゃんと設問を読んでいれば当たり前のことですね😇

という訳で以下。

def D(): N, Q = map(int, input().split()) call = 1 called_no_gone = BIT(N + 1) for _ in range(Q): ev, *x = input().split() if ev == "1": called_no_gone[call] = 1 call += 1 elif ev == "2": called_no_gone[int(x[0])] = 0 else: print(called_no_gone.get(1) - 1) test_all(D)

結果:#40007855

実行時間は大差ないですが、考える量は半分にできたと思います。

自由欄

pythonのsetでイケんじゃね?

さらに解説ではC++の順序付き集合型であるstd::setを使ってます。
pythonのset順序を保持しない(ちなみにdict挿入順を保持する)ので同じようには使えないなぁと思いつつダメ元で試してみたらACしました。

def D(): N, Q = map(int, input().split()) call = 1 called_no_gone = set() for _ in range(Q): ev, *x = input().split() if ev == "1": called_no_gone.add(call) call += 1 elif ev == "2": called_no_gone.remove(int(x[0])) else: print(next(iter(called_no_gone))) test_all(D)

結果:#40008022

ソース不明ですが、PyPyのset実装では挿入順を保持するっぽいです。
あくまで“挿入順”なのですが、本問ではその挿入順が数字のソート順と同じなのでACしたということなんでしょうね。

実装依存のACなので水物ですが、一応記録として置いておきます。

自由欄

解説を見る②

さらに解説では$O(N + Q)$の解法が紹介されています。
以下写経。

説明を読むと「そりゃそうだ」という内容なのですが、自力でここまで辿り着けるぐらいに読解力を向上させたい…

def D(): N, Q = map(int, input().split()) gone = [False] * (N + 1) ans = 1 for _ in range(Q): ev, *x = input().split() if ev == "1": pass elif ev == "2": gone[int(x[0])] = True else: while gone[ans]: ans += 1 print(ans) test_all(D)

結果:#40012269

自由欄

E - 2xN Grid

やってみよう!

E関数を完成させて「Run」ボタンをクリックしよう!

test_dataE = [ { "in":"""8 4 3 1 2 3 2 2 3 3 1 1 4 2 1 3 3 """, "out": """4 """ },{ "in":"""10000000000 1 1 1 10000000000 1 10000000000 """, "out": """10000000000 """ },{ "in":"""1000 4 7 19 79 33 463 19 178 33 280 19 255 33 92 34 25 19 96 12 11 19 490 33 31 """, "out": """380 """ }, ] def E(): pass test_all(E)

自由欄

コンテスト中

ランレングス圧縮というやつらしいです。
ランレングス圧縮(連長圧縮 / RLE)とは - 意味をわかりやすく - IT用語辞典 e-Words

while文内の19行目で絶対抜けることになるので、whileの条件が実質意味ないあたり、イケてないですね。

def E(): L, N1, N2 = map(int, input().split()) L1 = [list(map(int, input().split())) for _ in range(N1)] L2 = [list(map(int, input().split())) for _ in range(N2)] ans = 0 i1 = 0; i2 = 0; r1 = L1[0][1]; r2 = L2[0][1]; while i1 < N1 and i2 < N2: # print(i1, i2, ans) if L1[i1][0] == L2[i2][0]: ans += min(r1, r2) if r1 == r2: i1 += 1; i2 += 1; if i1 == N1 or i2 == N2: break r1 = L1[i1][1] r2 = L2[i2][1] elif r1 > r2: i2 += 1 r1 -= r2 r2 = L2[i2][1] else: i1 += 1 r2 -= r1 r1 = L1[i1][1] print(ans) test_all(E)

結果:#39875457

自由欄

解説を見る

公式解説は意味不明なのでスルーして、素朴な実装の解説を見ます。

考え方は自分の提出と同じなのですが、「『共通部分』の長さの出し方」がスマートだし全体的にも短くスッキリまとまってます。
日本語で理解すると:比較対象ブロックを含めた先頭からの長さの短い方 - 比較対象ブロックを除いた先頭からの長さの長い方
これも、言われると「確かに。。」となる内容なんですが、気付けんなぁ~😩

以下写経。

def E(): L, N, M = map(int, input().split()) A = [list(map(int, input().split())) for i in range(N)] B = [list(map(int, input().split())) for i in range(M)] ans, i, j, p, q = 0, 0, 0, 0, 0 while i < N and j < M: if A[i][0] == B[j][0]: ans += min(p + A[i][1], q + B[j][1]) - max(p, q) if p + A[i][1] < q + B[j][1]: p += A[i][1] i += 1 else: q += B[j][1] j += 1 print(ans) test_all(E)

自由欄

F - Sugar Water 2

やってみよう!

E関数を完成させて「Run」ボタンをクリックしよう!

test_dataF = [ { "in":"""3 1 1 1 2 4 1 1 4 1 4 3 3 """, "out": """50.000000000000000 """ },{ "in":"""2 2 2 6 4 10 1 5 8 9 6 """, "out": """62.500000000000000 """ },{ "in":"""4 5 10 5 4 1 6 7 4 9 8 2 2 5 6 6 7 5 3 8 1 """, "out": """54.166666666666664 """ }, ] def F(): pass test_all(F)

自由欄

コンテスト中

ダメ元で全探索したらやっぱりダメでした。

def F(): from itertools import product N, M, K = map(int, input().split()) AB = [list(map(int, input().split())) for _ in range(N)] CD = [list(map(int, input().split())) for _ in range(M)] c = list() for n, m in product(range(N), range(M)): c.append((AB[n][0] + CD[m][0]) / (sum(AB[n]) + sum(CD[m]))) print(sorted(c, reverse=True)[K - 1] * 100) test_all(F)

結果:#39881266

自由欄

解説を見る

「答えで二分探索」といえば競プロ典型 90 問001 - Yokan Partyですね!(ネタバレごめん!)
何を隠そう、このブログで初めて競プロの記事を書いたのがその問題でした。

本問では二分探索の着想だけでなく、式変形して「何の値を探すか」という発想がカギだったように思います。(私はどちらも思いつかなかったですが…)

で、解説で紹介されているのが「高橋くんと青木くんの砂糖水が濃度$x$に対して何グラム砂糖が多いか」という値を探すということ。
例えば、高橋くんの砂糖水$\alpha$は濃度$x$に対して3グラム砂糖が多く、青木くんの砂糖水$\beta$は濃度$x$に対して2グラム砂糖が少ない(-2グラム多い)としたら、砂糖水$\alpha$と$\beta$を混ぜた砂糖水$\gamma$の濃度は$x$より濃ゆくなるはずです。

そして水$w$グラムが与えられた場合、濃度$x$になるための砂糖$s'$の量は、

$$ \begin{align*} & \frac{s'}{w + s'} = x \\[10pt] \Leftrightarrow \;& s' = x(w + s') \\[10pt] \Leftrightarrow \;& s'(1 - x) = xw \\[10pt] \Leftrightarrow \;& s' = \frac{xw}{(1 - x)} \\[10pt] \end{align*}\\ $$

なので、後は実際の砂糖の量から$s'$を引けば前述の値が得られます。(ここまで書き下さないと私には理解できんとです😇)

高橋くんか青木くんの一方でこの値をソートしておき、もう一方から二分探索で混ぜて$x$より濃ゆくなる本数を数えるということなんですね~
かしこっ😩

以下写経。

def F(): from bisect import bisect N, M, K = map(int, input().split()) AB = [list(map(int, input().split())) for _ in range(N)] CD = [list(map(int, input().split())) for _ in range(M)] NG = 0 OK = 1 for i in range(100): x = (NG + OK) / 2 z = x / (1 - x) v = sorted([CD[j][0] - CD[j][1] * z for j in range(M)]) num = 0 for j in range(N): w = AB[j][0] - AB[j][1] * z num += M - bisect(v, -w) if num < K: OK = x else: NG = x print(OK * 100) test_all(F)

結果:#40164296

自由欄

自由欄