3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今回はPythonでABC428をABCEまで解くことができたので、その備忘録を纏めます。

コンテスト概要

AtCoder Beginner Contest 428

開催日:2025年10月18日 21:30-22:40


A - Grandma's Footsteps

この問題は「走って止まるを繰り返す高橋君が、X秒後にどれくらい進んでいるか」を求めるシンプルなシミュレーション問題

1. 1サイクルの長さを求める → 「走るA秒+止まるB秒」で cycle = A + B
2. X秒の中に何サイクル分あるかを求める → full = X // cycle (整数のわり算)
3. 余った時間(サイクル途中の時間を求める → remain = X % cycle
4. 走っている時間の合計を計算
・完全なサイクル分:A × full
・余った時間:min(remain, A)
→ 余った時間がA秒より短いなら、その分だけ走る。
→ もし余りがA秒以上なら、A秒分は確実に走っている。
5. 走った距離を求める → 1秒あたりSメートル × 総走行時間

S, A, B, X = map(int, input().split())

cycle = A + B
full = X // cycle
remain = X % cycle

distance = S * (A * full + min(remain, A))
print(distance)

B - Most Frequent Substrings

方針📝

Sから長さKの部分文字列を全部取り出し、それぞれの出現回数を数える。
その回数にあたる部分文字列をすべて集めて、辞書順で並べる。

from collections import Counter
N, K = map(int,input().split())
S = input().strip()

# Sから長さKの部分文字列をすべて取り出す
sub = [S[i:i+K] for i in range(N-K+1)]
c = Counter(sub)

x = max(c.values())

max_sub = sorted([t for t, c in c.items() if c == x])
print(x)
print(*max_sub)

PythonならばcollectionsCounterを使うことで楽に出現回数の記録が可能。


C - Brackets Stack Query

クエリが順番に来て、

  • 」または「)」を追加する(1 c
  • 末尾の1文字を削除する(2

という操作をして、操作をする度に今の文字列Sが良い括弧列になっているのかを判定する問題。

良い括弧列とは?

⭕️良い例

  • ()
  • (())
  • (()())
  • 空文字列(何もない)

❌悪い例

  • )( (最初に閉じている)
  • (()(閉じ忘れ)
  • ))) (閉じすぎ)

方針📝

1. 文字列の末尾に「(」や「)」を追加・削除するたびに
現在のbalance(開きと閉じの差を更新
2. 今までの最小balance(min_balance)を記録。
これで「途中でマイナスになったかどうか」を判定。
3. 各クエリ後に

  • balance==0(開きと綴じが一致)
  • min_balance > 0(途中で閉じすぎていない)

の2つを満たせば、「Yes」、そうでなければ「No

入力から準備まで
  • mは現在の文字列をリストで表したもの
  • balanceは「(+1、「)」を−1した合計
  • min_balanceは今までのbalanceの最小値(途中で閉じすぎたかを見る)
  • historyは各状態(balanceとmin_balance)を記録しておく履歴
Q = int(input())
m = []
balance = 0
min_balance = 0

history = [(0, 0)]
①追加クエリのとき(1 c)

◽️処理の流れ

  • 文字cmに追加
  • もし「(」なら balance += 1
    もし「)」なら balance -= 1
  • min_balance を更新(途中でマイナスになったら記録)
  • 状態 (balance, min_balance) を履歴(history)に保存
if query[0] == "1":
    c = query[1]
    m.append(c)
    if c == "(":
        balance += 1
    else:
        balance -= 1
    min_balance = min(min_balance, balance)
    history.append((balance, min_balance))
②追加クエリのとき(2)

◽️処理の流れ

  • 文字列の末尾を削除
  • 履歴(history)からも削除
  • balanceとmin_balanceを、ひとつ前の状態に戻す
else:
    m.pop()
    history.pop()
    balance, min_balance = history[-1]
③判定部分
  • 開きと綴じの数が同じ
  • 途中で閉じすぎていない

上記2点を確認

if balance == 0 and min_balance >= 0:
    print("Yes")
else:
    print("No")

提出コード

Q = int(input())
m = []
balance = 0
min_balance = 0

history = [(0, 0)]

for _ in range(Q):
    query = list(input().split())
    if query[0] == "1":
        c = query[1]
        m.append(c)
        if c == "(":
            balance += 1
        else:
            balance -= 1
        min_balance = min(min_balance, balance)
        m.append(c)
        history.append((balance, min_balance))
    else:
        m.pop()
        history.pop()
        balance, min_balance = history[-1]
    
    if balance == 0 and min_balance >= 0:
        print("Yes")
    else:
        print("No")

E - Wind Cleaning

「木」というのはループのないグラフ(繋がっているけど閉じていない)のこと。木の頂点vをひとつ選んだ時に、頂点から一番遠い頂点の番号を求める問題。

方針📝

「木の直径」

木の中で最も遠い2つの点(距離が最大のペア)を直径という。
この直径の両端をp1,p2とした時にどの頂点vからも一番遠い頂点 は必ずp1p2のどちらになるということを踏まえて問題を解いて行きます。

1. 1番遠い点p1を探す
2. p1から一番遠い点p2を探す(これで直径の両端を確定)
3. p1からの距離dist1p2からの距離dist2をそれぞれ求める
4. 各頂点vについて

  • dist1[v]>dist2[v]p1が遠い
  • dist1[v]<dist2[v]p2が遠い
  • 同じならば番号の大きい方

①グラフの作成

N = int(input())
g = [[] for _ in range(N + 1)]
for _ in range(N - 1):
    a, b = map(int, input().split())
    g[a].append(b)
    g[b].append(a)

②BFS(幅優先探索)

BFSとは、「近い順に探索していく」方法。
ここでは、dist[i] に「startからiまでの距離」を記録します。

from collections import deque

def bfs(start):
    dist = [-1] * (N + 1)
    dist[start] = 0
    q = deque([start])

    while q:
        v = q.popleft()
        for nxt in g[v]:
            if dist[nxt] == -1:
                dist[nxt] = dist[v] + 1
                q.append(nxt)

③一番遠い頂点を見つける

一番距離が遠い頂点をbest_iに保存
同じ距離なら、番号が大きい方を採用

    best_i = 1
    for i in range(2, N + 1):
        if dist[i] > dist[best_i]:
            best_i = i
        elif dist[i] == dist[best_i] and i > best_i:
            best_i = i
    return best_i, dist

④BFSの実行と拡張点の答えを出力

  • まず「1」から最も遠い点 → p1
  • つぎに p1 から最も遠い点 → p2
    → これで木の「直径の両端」が確定!
  • p1 からの距離と p2 からの距離をdist1,dist2

各頂点vについて:

  • p1の方が遠ければ → p1
  • p2の方が遠ければ → p2
  • 同じなら → 番号が大きい方
p1, _ = bfs(1)
p2, dist1 = bfs(p1)
_, dist2 = bfs(p2)

for v in range(1, N + 1):
    if dist1[v] > dist2[v]:
        print(p1)
    elif dist1[v] < dist2[v]:
        print(p2)
    else:
        print(max(p1, p2))

提出コード

import sys
from collections import deque

N = int(input())
g = [[] for _ in range(N + 1)]
for _ in range(N - 1):
    a, b = map(int, input().split())
    g[a].append(b)
    g[b].append(a)


def bfs(start):
    dist = [-1] * (N + 1)
    dist[start] = 0
    q = deque([start])

    while q:
        v = q.popleft()
        for nxt in g[v]:
            if dist[nxt] == -1:
                dist[nxt] = dist[v] + 1
                q.append(nxt)

    best_i = 1
    for i in range(2, N + 1):
        if dist[i] > dist[best_i]:
            best_i = i
        elif dist[i] == dist[best_i] and i > best_i:
            best_i = i

    calc = best_i
    return calc, dist


p1, _ = bfs(1)
p2, dist1 = bfs(p1)
_, dist2 = bfs(p2)

for v in range(1, N + 1):
    if dist1[v] > dist2[v]:
        print(p1)
    elif dist1[v] < dist2[v]:
        print(p2)
    else:
        print(max(p1, p2))

結果

ABCDE 5完
順位 1441st / 12157
パフォーマンス 1360
レーティング 1078 → 1113 (+35) Highest更新!

感想

今回はD問題を解くことができませんでした。(水色diffらしい)
難しい問題をうまく避けてE問題をしっかり通すことができ、Highestを更新できたことは良かったです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?