LoginSignup
13
9

More than 1 year has passed since last update.

【AtCoder解説】PythonでABC225のA,B,C,D問題を制する!

Last updated at Posted at 2021-10-31

ABC225A,B,C,D問題を、Python3でなるべく丁寧に解説していきます。

ただ解けるだけの方法ではなく、次の3つのポイントを満たす解法を解説することを目指しています。

  • シンプル:余計なことを考えずに済む
  • 実装が楽:ミスやバグが減ってうれしい
  • 時間がかからない:パフォが上がって、後の問題に残せる時間が増える

ご質問・ご指摘はコメントツイッターマシュマロ、Discordサーバーまでお気軽にどうぞ!

Twitter: u2dayo
マシュマロ: https://marshmallow-qa.com/u2dayo
ほしいものリスト: https://www.amazon.jp/hz/wishlist/ls/2T9IQ8IK9ID19?ref_=wl_share
Discordサーバー(質問や記事の感想・リクエストなどどうぞ!) : https://discord.gg/jZ8pkPRRMT

よかったらLGTM拡散していただけると喜びます!

目次

ABC225 まとめ
A問題『Distinct Strings』
B問題『Star or Not』
C問題『Calendar Validator』
D問題『Play Train』

アプリ AtCoderFacts を開発しています

コンテストの統計データを見られるアプリ『AtCoderFacts』を作りました。
現在のところ、次の3つのデータを見ることができます。

  • レート別問題正解率
  • パフォーマンス目安
  • 早解きで上昇するパフォーマンス

今後も機能を追加していく予定です。使ってくれると喜びます。

ABC225 まとめ

全提出人数: 6585人

パフォーマンス

パフォ AC 点数 時間 順位(Rated内)
200 AB------ 300 32分 4984(4739)位
400 ABC----- 600 124分 4109(3866)位
600 ABC----- 600 65分 3392(3150)位
800 ABC----- 600 19分 2668(2428)位
1000 ABCD---- 1000 81分 2005(1774)位
1200 ABCD---- 1000 55分 1465(1238)位
1400 ABCD---- 1000 37分 1050(827)位
1600 ABCD---- 1000 19分 730(521)位
1800 ABCDE--- 1500 87分 481(303)位
2000 ABCDE--- 1500 63分 317(160)位
2200 ABCDE--- 1500 48分 212(78)位
2400 ABCDE--- 1500 37分 148(35)位

色別の正解率

人数 A B C D E F G H
2699 96.4 % 76.9 % 33.8 % 8.3 % 0.3 % 0.0 % 0.0 % 0.0 %
1281 99.2 % 98.4 % 80.4 % 38.2 % 0.6 % 0.1 % 0.0 % 0.0 %
970 99.2 % 99.0 % 91.8 % 75.5 % 7.2 % 0.4 % 0.0 % 0.0 %
559 99.6 % 99.6 % 98.2 % 94.1 % 31.3 % 0.4 % 0.0 % 0.0 %
355 99.2 % 99.2 % 98.9 % 98.3 % 64.2 % 3.7 % 2.8 % 0.6 %
189 93.1 % 92.6 % 93.1 % 92.6 % 68.8 % 12.2 % 18.0 % 1.6 %
35 97.1 % 97.1 % 94.3 % 94.3 % 91.4 % 40.0 % 51.4 % 2.9 %
28 100.0 % 100.0 % 100.0 % 96.4 % 92.9 % 64.3 % 82.1 % 50.0 %

表示レート、灰に初参加者は含めず

A問題『Distinct Strings』

問題ページA - Distinct Strings
コーダー正解率:96.4 %
コーダー正解率:99.2 %
コーダー正解率:99.2 %

入力

$S$ : 英小文字のみからなる長さ $3$ の文字列

考察

$S$ に何種類の文字が出てくるかで答えが決まります。

文字の種類数で答えがいくつになるかは、すべてサンプルに答えがあります。

$1$ 種類 : 1 (①①①)
$2$ 種類 : 3 (②①①・①②①・①①② ${}{3} \mathrm{C}{1} = 3$ 通り)
$3$ 種類 : $6$ (①②③・①③②・②①③・②③①・③①②・③②① $3!=3\times{2}\times{1}=6$ 通り)

実装

if文でがんばって文字の種類数を求めてもいいですが、len(set(S))と書くことで文字の種類数が簡単にわかります。setで重複を省いたあと、lenで重複なしの要素数=種類数を求めています。

コード

S = input()
N = len(set(S))

if N == 1:
    print(1)
elif N == 2:
    print(3)
else:
    print(6)

B問題『Star or Not』

問題ページB - Star or Not
コーダー正解率:76.9 %
コーダー正解率:98.4 %
コーダー正解率:99.0 %

入力

$N$ : 木の頂点数
$a_i,b_i$ : $i$ 番目の辺は $a_i$ と $b_i$ を結んでいる

考察

『ある $1$ つの頂点から、他のすべての頂点に $1$ 本ずつ辺が出ている木』かどうか判定すればいいです。

言い換えると、『他の $N-1$ 個の頂点に $1$ 本ずつ辺が出ている頂点がある』かどうか判定すればいいです。

各頂点が何回辺に出現するかカウントして、$N-1$ 回出てくる頂点があれば Yes です。

コード

N = int(input())
counter = [0] * (N + 1)

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

print("Yes" if N - 1 in counter else "No")

C問題『Calendar Validator』

問題ページC - Calendar Validator
コーダー正解率:33.8 %
コーダー正解率:80.4 %
コーダー正解率:91.8 %

入力

$N,M$ : 矩形(長方形)領域の行数・列数
$B_{i,j}$ : 矩形領域の、$i$ 行目 $j$ 列目に書いてある数字

考察

行列 $B$ が、カレンダーから矩形領域を向きを変えずに切り出したものである条件は、以下の $3$ つです。

  • カレンダーの一番左上が $1$ で始まっている
  • 左右に隣り合う要素の差は $1$
  • 上下に隣り合う要素の差は $7$

要素の差はforループで確かめればいいです。

一番左上が $1$ になる条件は、 要素を $7$ で割った余りが $0$ の要素が切り出した領域の一番右に来ていることです。(日曜日の右に月曜日があるカレンダーは、左上が月曜日になりません)

コード

def judge():
    # 左上が1になりうるか確認
    for col in range(M):
        if B[0][col] % 7 == 0 and col != M - 1:
            return False

    # 左右に隣り合う要素の差が1か確認
    for row in range(N):
        for col in range(M - 1):
            if B[row][col] + 1 != B[row][col + 1]:
                return False

    # 上下に隣り合う要素の差が7か確認
    for row in range(N - 1):
        if B[row][0] + 7 != B[row + 1][0]:
            return False
    return True


N, M = map(int, input().split())
B = []
for _ in range(N):
    _b = list(map(int, input().split()))
    B.append(_b)

print("Yes" if judge() else "No")

D問題『Play Train』

問題ページD - Play Train
コーダー正解率:8.3 %
コーダー正解率:38.2 %
コーダー正解率:75.5 %

入力

$N$ : 電車の数
$Q$ : クエリの数

クエリ $1$
$x,y$ : 電車 $x$ の後部と、電車 $y$ の前部を連結させる(電車 $x$ の後ろに電車 $y$ が来るように連結する)

クエリ $2$
$x,y$ : 電車 $x$ の後部と、電車 $y$ の前部を分離させる(分離したあと、電車 $x$ が最後尾で、電車 $y$ が先頭になる)

クエリ $3$
$x$ : 電車 $x$ が含まれる連結成分の電車の番号を、先頭から順番にすべて出力する

ただし、クエリ $3$ で出力する電車の番号の個数の合計は、$10^6$ 以下

考察

双方向連結リストというデータ構造を実装すればいいです。

各電車ごとに、『前にある電車の番号』『後ろにある電車の番号』を記録しておきます。先頭で前に電車がない、最後尾で後ろに電車がない場合は、それがわかるように -1 を入れておきます。

クエリ $3$ が来たら、『前にある電車の番号』をたどって、先頭の電車まで移動します。その後、『後ろにある電車の番号』をたどって、順番に出力していけばいいです。

コード

def main():
    N, Q = map(int, input().split())
    _next = [-1] * (N + 1)  # すぐ後ろの電車の番号
    _prev = [-1] * (N + 1)  # すぐ前の電車の番号

    for _ in range(Q):
        query = list(map(int, input().split()))
        q = query[0]
        if q == 1:
            x, y = query[1:]
            _next[x] = y  # xの後ろにyがくる
            _prev[y] = x  # yの前にxがくる
        elif q == 2:
            x, y = query[1:]
            _next[x] = -1  # xが新しい最後尾
            _prev[y] = -1  # yが新しい先頭
        else:
            x = query[1]
            ans = []

            curr = x

            # まず電車を先頭までたどります
            while _prev[curr] != -1:
                curr = _prev[curr]

            # 先頭から最後尾までたどっていきます
            while curr != -1:
                ans.append(curr)
                curr = _next[curr]

            print(len(ans), *ans)  # 連結成分の大きさも出力することに注意


if __name__ == '__main__':
    main()
13
9
2

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
13
9