LoginSignup
12
12

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-09-26

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

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

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

ご質問・ご指摘はコメントツイッターマシュマロまでどうぞ!

Twitter: u2dayo
マシュマロ: [https://marshmallow-qa.com/u2dayo]
ほしいものリスト: https://www.amazon.jp/hz/wishlist/ls/2T9IQ8IK9ID19?ref_=wl_share

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

目次

ABC220 まとめ
A問題『Find Multiple』
B問題『Base K』
C問題『Long Sequence』
D問題『FG operation』

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

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

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

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

ABC220 まとめ

全提出人数: 7469人

パフォーマンス

パフォ AC 点数 時間 順位(Rated内)
200 ABC----- 600 117分 5608(5397)位
400 ABC----- 600 49分 4625(4419)位
600 ABC----- 600 25分 3826(3621)位
800 ABCD---- 1000 86分 3006(2803)位
1000 ABCD---- 1000 46分 2253(2050)位
1200 ABCD---- 1000 19分 1632(1432)位
1400 ABCD-F-- 1500 78分 1141(955)位
1600 ABCD-F-- 1500 42分 785(602)位
1800 ABCDEF-- 2000 94分 513(351)位
2000 ABCDEF-- 2000 73分 326(187)位
2200 ABCDEF-- 2000 57分 202(92)位
2400 ABCDEF-- 2000 41分 132(43)位

色別の正解率

人数 A B C D E F G H
3128 94.4 % 77.4 % 60.9 % 10.1 % 0.6 % 2.5 % 0.1 % 0.0 %
1344 99.5 % 97.6 % 95.8 % 52.8 % 3.6 % 8.3 % 0.0 % 0.0 %
1041 99.7 % 98.8 % 97.9 % 87.0 % 8.9 % 27.3 % 0.2 % 0.0 %
653 99.7 % 98.9 % 98.8 % 97.1 % 34.0 % 55.0 % 1.2 % 0.0 %
389 99.2 % 99.0 % 99.5 % 98.7 % 69.7 % 85.1 % 5.4 % 0.0 %
165 92.7 % 90.3 % 90.3 % 90.9 % 78.8 % 87.3 % 26.1 % 3.0 %
37 91.9 % 91.9 % 91.9 % 91.9 % 89.2 % 86.5 % 73.0 % 13.5 %
20 90.0 % 85.0 % 90.0 % 85.0 % 80.0 % 85.0 % 60.0 % 45.0 %

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

A問題『Find Multiple』

問題ページA - Find Multiple
コーダー正解率:94.4 %
コーダー正解率:99.5 %
コーダー正解率:99.7 %

入力

$A,B,C$ : $0$ 以上 $1000$ 以下の整数

考察

解法1. forループで全探索

$A$ から $B$ までの整数をforループで全探索します。$C$ の倍数(=$C$ で割りきれる数=$C$ で割った余りが $0$ の数)が $1$ つでも見つかればそれを出力して、見つからなければ $-1$ を出力します。

解法2. 算数

『$B$ 以下で最大の $C$ の倍数』は $\lfloor{\frac{B}{C}}\rfloor \times{C}$ で求められます。($B$ を $C$ で割って切り捨てて $C$ を掛ける)これが $A$ 以上かどうか判定して、もしそうならそれを出力、そうでなければ $A$ 以上 $B$ 以下に $C$ の倍数はないので $-1$ を出力します。

例えば、$A=17,B=31,C=5$とします。$\lfloor{\frac{B}{C}}\rfloor \times{C}=\lfloor{\frac{31}{5}}\rfloor\times{5}=6\times{5}=30$です。これは $17$ 以上なので、$30$ を出力します。正の $5$ の倍数は $5,10,15,20,25,30,35,\dots$ で、$31$ 以下で最大の正の $5$ の倍数はたしかに $30$ です。

コード

解法1. forループで全探索

def solve():
    for i in range(A, B + 1):
        if i % C == 0:
            return i
    return -1  # A以上B以下の整数にCの倍数がなかったので、-1を返します


A, B, C = map(int, input().split())
print(solve())

解法2. 算数

A, B, C = map(int, input().split())
x = B // C * C  # xはB以下で最大のCの倍数
print(x)
print(x if x >= A else -1)

B問題『Base K』

問題ページB - Base K
コーダー正解率:77.4 %
コーダー正解率:97.6 %
コーダー正解率:98.8 %

入力

$K$ : $A,B$ が $K$ 進法で与えられる
$A,B$: $K$ 進法表記の整数

実装

$K$ 進法表記の整数 $A,B$ それぞれを$10$ 進法の整数に変換できれば、あとは $A\times{B}$ をするだけで解けます。

解法1. int関数を使う方法

Pythonの組み込み関数int関数を使うと、$K$ 進法の数値を $10$ 進法の数値に 変換することができます。

解法2. 自分で変換する

頑張って自分で変換します。$K$ 進法表記の下から $i$ 桁目が $x$ ならば、$10$ 進法表記では $x\times{K^{i}}$ 増えます。桁ごとに足し合わせればいいです。

コード

解法1. int関数を使う方法

K = int(input())
A, B = input().split()  # int関数で基数変換をするために、A, Bはstring(文字列)で受け取る必要があります
ans = int(A, K) * int(B, K)
print(ans)

解法2: 自分で変換する

def convert(x, k):
    ret = 0
    i = 0
    while x > 0:
        ret += (x % 10) * (k ** i)
        x //= 10
        i += 1
    return ret


K = int(input())
A, B = map(int, input().split())
ans = convert(A, K) * convert(B, K)
print(ans)

C問題『Long Sequence』

問題ページC - Long Sequence
コーダー正解率:60.9 %
コーダー正解率:95.8 %
コーダー正解率:97.9 %

入力

$N$ : $A$ の長さ
$A_i$ : $A$ の $i$ 番目の要素
$X$ : 和が初めて $X$ を超えるのは何項目か求める(以上ではない)

考察

$1$ 項ずつ $A$ の要素を $X$ が超えるまで足し合わせるとTLEになります。(例えば $N=2, A_1=1,A_2=1, X=10^{18}$ だと、答えは $10^{18}$ です)

そこで、まずは $A$ の $N$ 要素全体を何回使うかを、計算で求めます。これは、$A$ の総和を $sum_A$ とすると、$\lfloor{\frac{X}{sum_A}}\rfloor$ 回 です。($X$ を $sum_A$ で割って切り捨てた回数)

それから、$A$ の一部をどこまで使うと$X$ を超えるか、forループで足し合わせて求めればいいです。

計算量は $O(N)$ です。

コード

N = int(input())
A = list(map(int, input().split()))
X = int(input())

ans = 0  # 現在の項数
S = 0  # 現在の総和
sum_A = sum(A)

c = X // sum_A  # A全体を何回使うか
ans += c * N  # A全体を何回使うか×Aの項数
S += c * sum_A  # A全体を何回使うか×Aの総和

for a in A:
    S += a
    ans += 1
    if S > X:  # Xを超えるときなので > です
        break

print(ans)

D問題『FG operation』

問題ページD - FG operation
コーダー正解率:10.1 %
コーダー正解率:52.8 %
コーダー正解率:87.0 %

入力

$N$ : $A$ の長さ
$A_i$ : $A$ の $i$ 番目の数($0$ 以上 $9$ 以下の整数)

考察

二次元の動的計画法(DP)で解きます。

状態

$A$ の最初の要素を $0$ 要素目として数えることにします。

$dp[i][j]$ : $A$ の $i$ 要素目まで見て、一番左の要素が $j$ である操作の方法が何通りあるか

答え

$K=0,\,1\,,\dots\,,9$ に対して、$dp[N-1][K]$ がそれぞれの答えです。

初期状態

$dp[0][A_0] = 1$
$dp[i][j]=0\,(上以外のすべてのi,j)$

遷移

$i=0$ の状態(まだ一度も操作をしていない、$A$ が最初から変わっていないとき)は左端が$A_0$ の $1$ 通りで確定しているので、$A_0$ と $A_1$ に操作を行う、$i=1$ から状態を決めていきます。

状態 $dp[i-1][j]$ からの遷移は以下の通りです。

$f=(j+A_{i})\,\%\,10$
$g=(j\times{A_i})\,\%\,10$

とします。(それぞれ操作F,Gを行ったあと、新たに一番左になる数字です)

$dp[i][f]\,+=dp[i-1][j]$
$dp[i][g]\,+=dp[i-1][j]$

コード

def main():
    MOD = 998244353

    N = int(input())
    A = list(map(int, input().split()))

    dp = [[0] * 10 for _ in range(N)]
    dp[0][A[0]] = 1

    for i in range(1, N):
        for j in range(10):
            f = (j + A[i]) % 10  # 一番左のjと、二番目に左のA[i]に操作Fをしたとき、新たに一番左になる数字
            g = (j * A[i]) % 10  # 操作G
            dp[i][f] += dp[i - 1][j]
            dp[i][g] += dp[i - 1][j]
            dp[i][f] %= MOD
            dp[i][g] %= MOD

    for i in range(10):
        print(dp[N-1][i])


if __name__ == '__main__':
    main()
12
12
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
12
12