5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ABC330をPythonで解いてみたよ。(A~E問題)

Last updated at Posted at 2023-11-25

AtCoder Beginners Contest 330 (ABC330) をPythonで解きました。
見やすいコードを書く練習も兼ねてます。

A - Counting Passes

問題

$L$ 点以上を取った人は何人いますか?

考察

for文をつかって リスト $A$ の要素を1つずつ見て、要素が $L$ 以上だったら変数 $ans$ を $1$ だけプラスします。

コード

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

ans = 0
for a in A:
    if a >= L:
        ans += 1
print(ans)
内包表記をつかった解法

内包表記をつかうと、for文で4行くらいつかっていたコードを1行でスッキリまとめられます。

ここでは内包表記については詳しく書きませんが、「リスト内包表記」などと調べると詳しい記事がたくさん出てきます。

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

ans = len([a for a in A if a >= L])
print(ans)

B - Minimize Abs 1

問題

考察

問題の読解が難しいですが、要するに、リスト $A$ の各要素 $a$ について下の場合分けをすればいいです。

  • $a<=L$ のとき、$L$
  • $L<a<R$ のとき、 $a$
  • $R<=a$ のとき、 $R$

下のコードでは、上の場合分けを関数化して実装しています。

コード

def f(a):
    if a <= L:
        return L
    if R <= a:
        return R
    return a


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

ans_lst = []
for a in A:
    ans_lst.append(f(a))
print(*ans_lst)

C - Minimize Abs 2

問題

$|x^2+y^2-D|$ を最小にしてね。

考察

$x$ が決まっていたとします。
$|x^2+y^2-D|$ を最小にしたいので、$y=\sqrt{|D-x^2|}$ にすればいいです。ただし $y$ は整数なのできちんと$y=\sqrt{|D-x^2|}$ にはならないかもしれないですが、それでもだいたい $y=\sqrt{|D-x^2|}$ になります。
$(2\times 10^6)^2=4\times 10^{12} > 2 \times 10^{12} >=D$ なので、 $x$ を $0$ から $2\times 10^6$ までfor文で動かして、先ほどの計算をすればいいです。

下のコードでは、math.isqrt(abs(D-x*x)) で $y=\sqrt{|D-x^2|}$ の整数部分を求めて、その $±10$ で $|x^2+y^2-D|$ の値を調べています。
(本来は、その整数部分を $i$ としたとき $i$ と $i+1$ だけ調べればいいですが、コンテストではACするのが優先なので雑に広めにチェックしています。)

コード

import math

D = int(input())

ans = 1 << 60
for x in range(0, 2_000_000):
    yy = math.isqrt(abs(D - x * x))
    for y in range(yy - 10, yy + 11):
        ans = min(ans, abs(x * x + y * y - D))

D - Counting Ls

問題

$3$ つの'o'で、「そのうち $2$ つだけが同じ行にある」と 「そのうち $2$ つだけが同じ列にある」 の条件をどちらも満たしているような組はいくつありますか?
https://atcoder.jp/contests/abc330/tasks/abc330_d

考察

図で表すと、こんな感じです。

image.png

こういうL字になるような $3$ つの 'o'を選びたくて、その選び方は何パターンありますか?という問題。

このL字のかどっこで全探索する方針でいきます。
$(i,j)$ の座標が 'o' のとき、

  • 行 $i$ のなかで、(i,j) 以外の'o' の個数 ($=r$ とします。)
  • 列 $j$ のなかで、(i,j) 以外の'o' の個数 ($=c$ とします。)
    の $2$ つが分かると、 $(i,j)$ がL字のかどっこになるような選び方のパターンは $r \times c$ になります。

各行・各列の'o'の個数をあらかじめ計算しておけば、上の $r \times c$ は$O(1)$ で求められるので、全体で $O(N^2)$ で解くことができます。

コード

N = int(input())
S = [input() for _ in range(N)]

row = [0 for _ in range(N)]
col = [0 for _ in range(N)]
for i in range(N):
    for j in range(N):
        if S[i][j] == "o":
            row[i] += 1
            col[j] += 1

ans = 0
for i in range(N):
    for j in range(N):
        if S[i][j] == "o":
            r = row[i] - 1
            c = col[j] - 1
            ans += r * c
print(ans)

E - Mex and Update

問題

リスト $A$ の一点更新クエリが $Q$ 個与えられます。
クエリが来るたびに、 $A$ のmexを求めてね。

考察

mexは、$0$ 以上の整数の中でリストに含まれないような最小の整数です。
リスト $A$ の長さは $N$ で与えられています。
たとえば $A=[0,1,\cdots N-1]$ のとき、mexは $N$ になります。ちなみに mexの最大値は $N$ になります。

collections.Counter をつかって、リスト $A$ にある要素の数を管理します。
また、SortedSetをつかって、常にリスト $A$ の中にない整数を管理します。

クエリが来るたびに、SortedSetの中身(=リスト $A$ にない整数セット)を調整して、その中で最小の整数を出力します。

コメントもつけているので、細かい実装は下のコードを見てください。

コード

from collections import Counter

"""
tatyamさん作の、SortedSetです。
使わせていただき、ありがとうございます!
https://github.com/tatyam-prime/SortedSet/blob/main/SortedSet.py

・使い方(個人的まとめ)
s=SortedSet()
s.a: SortedSetの中身を返す。
len(s), x in s, x not in s: リストと同じ要領で使える。
s.add(x): xを追加してTrueを返す。ただしxがすでにs内にある場合、xは追加せずにFalseを返す。
s.discard(x): xを削除してTrueを返す。ただしxがs内にない場合、何もせずにFalseを返す。
s.lt(x): xより小さい最大の要素を返す。もし存在しないなら、Noneを返す。
s.le(x): x 以下の 最大の要素を返す。もし存在しないなら、Noneを返す。
s.gt(x): xより大きい最小の要素を返す。もし存在しないなら、Noneを返す。
s.ge(x): x 以上の 最小の要素を返す。もし存在しないなら、Noneを返す。
s.index(x): xより小さい要素の数を返す。
s.index_right(x): x以下の要素の数を返す。

・使い方URL
https://github.com/tatyam-prime/SortedSet
"""
# (SortedSetのコード省略。 上のURLからコピペしてね。)

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

# se: 0~Nの中でリストAの中にない整数のセット。
se = SortedSet([i for i in range(N + 1)])
for a in A:
    se.discard(a)

counter = Counter(A)

for _ in range(Q):
    i, x = map(int, input().split())
    i -= 1
    prev = A[i]
    A[i] = x
    counter[prev] -= 1
    counter[x] += 1
    if counter[prev] == 0:
        # リストAからprevがなくなったので、mexの値の候補にprevが入る。
        se.add(prev)
    # リストAにxがあるので、seの中にxがあった場合はxを取り出す。
    # (SortedSetの中に無い要素をdiscardしても、エラーはおきません。)
    se.discard(x)
    print(se.ge(0))  # seの中にある0以上の最小の整数を出力する。

おまけ

sortedcontainersの中にも、同じ機能を持つSortedSetがあります。

from sortedcontainers import SortedSet

se = SortedSet()

ですが、このSortedSetよりもtatyamさん作のSortedSetの方がはやく、上の解答コードをsortedcontainersのSortedSetに置き換えて提出するとTLEになります。(提出コード(TLE))
基本的にはtatyamさん作のSortedSetをつかう方がいいのかなと思います。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?