AtCoder Beginner Contest 347をPythonで解く

投稿日:  更新日:2024/03/31

Atcoder Python

B!
Daniel AgreloによるPixabay(https://pixabay.com/)からの画像

こんにちは, Shinoryoです.

今回はAtCoder Beginner Contest 347を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Divisible

空のリストquotientsを用意します. リストAをループして, 各要素Aiに対して次の操作を行います.

  • Aikで割り切れる場合:quotientsAi/kを追加する.
  • Aikで割り切れない場合:何もしない.
# 入力
n, k = map(int, input().split())
a = list(map(int, input().split()))
# リストaをループして、kで割り切れる要素の商を計算し、quotientsに追加する
quotients = []
for a_i in a:
if a_i % k == 0:
quotients.append(a_i // k)
# 出力
print(*quotients)

B - Substring

Sの部分文字列を全探索して, 何種類あったかを数え上げればよいです. Sの部分文字列を取得するには, 文字列のスライスを用います. 重複なく用いるには, 集合(set)に追加していって, 最後にその要素数を数えます.

# 入力
s = input()
# 文字列sの部分文字列を格納するセットを作成
substring_set = set()
# 全ての部分文字列を探索し、substring_setに追加する
for i in range(len(s)):
for j in range(i + 1, len(s) + 1):
substring_set.add(s[i:j])
# 出力
print(len(substring_set))

C - Ideal Holidays

1週間の何日目に予定があるかが重要なので, Dの各要素を(A+B)で割った余りを用意し, ソートしてしまいましょう.

高橋くんの予定が全て休日であるということは, ある予定とその次の予定の間に全ての平日が含まれればよいです.

例えば, 1週間のうち2日が休日で5日が平日の例を考えてみます. 次の表のようになっている場合, 3, 4, 5, 6, 0日目が平日であれば, 高橋くんの予定は全て休日です.

次の表のようになっている場合, 1, 2, 3, 4, 5日目が平日であれば, 高橋くんの予定は全て休日です.

次の表のようになっている場合, 平日をどのように設定しても, 高橋君の予定のどれかが平日になってしまいます.

# 入力
n, a, b = map(int, input().split())
d = list(map(int, input().split()))
# 週の長さ
week_length = a + b
# 予定のある日を1週間の何日目かに変換し、重複を排除する
days = [d_i % week_length for d_i in d]
distinct_days = list(set(days))
distinct_days.sort()
distinct_days_count = len(distinct_days)
# 「門番」を加える
distinct_days.append(distinct_days[0] + week_length)
# 予定のある日の間隔がbを超えるかどうかをチェック
for i in range(distinct_days_count):
if distinct_days[i + 1] - distinct_days[i] > b:
print("Yes") # 予定のある日がb以上離れている場合、Yesを出力して終了
exit()
print("No") # 予定のある日がb以上離れていない場合、Noを出力

あるいは, 全ての予定が休日の範囲に収まればよいです.

from collections import deque
# 入力
n, a, b = map(int, input().split())
d = list(map(int, input().split()))
week_length = a + b
# 予定のある日を1週間の何日目かに変換し、重複を排除する
days = [d_i % week_length for d_i in d]
distinct_days = list(set(days))
distinct_days.sort()
distinct_days_deque = deque(distinct_days)
# 予定のある日の範囲がaを超えないかどうかをチェック
for _ in range(n):
if distinct_days_deque[-1] - distinct_days_deque[0] < a:
print("Yes") # 予定のある日の範囲がa以内の場合、Yesを出力して終了
exit()
distinct_days_deque.rotate(-1)
distinct_days_deque[-1] += week_length
print("No") # 予定のある日の範囲がaを超える場合、Noを出力

D - Popcount and XOR

基本的には, 公式解説どおりです.

n0,0, n0,1, n1,0, n1,1が非負整数であるための条件だけ補足します. a, b, cが整数であることを用いて, 次のように考えることができます.

  1. n0,0が非負である (1)60a+b+c20a+b+c120
  2. n0,0が整数である a+b+c2の倍数 (2)a+b+c0 (mod 2)
  3. n0,1が非負である (3)a+b+c20ab+c
  4. n0,1が整数である a+b+c2の倍数 (4)a+b+c0 (mod 2)a+b+c2a0 (mod 2)
  5. n1,0が非負である (5)ab+c20bc+a
  6. n1,0が整数である ab+c2の倍数 (6)ab+c0 (mod 2)a+b+c2b0 (mod 2)
  7. n1,1が非負である (7)a+bc20ca+b
  8. n1,1が整数である a+bc2の倍数 (8)a+bc0 (mod 2)a+b+c2c0 (mod 2)

最終的に, a+b+c120, a+b+c0 (mod 2), ab+c, bc+a, ca+bにまとめることができます.

# 入力
a, b, input_c = map(int, input().split())
c = input_c.bit_count()
# a, b, cが条件を満たすかを確認する
if (a + b + c) % 2 == 1:
print(-1)
elif a + b + c > 120:
print(-1)
elif a > b + c:
print(-1)
elif b > c + a:
print(-1)
elif c > a + b:
print(-1)
# 条件を満たすならば、xとyを計算する
else:
i = 60 - (a + b + c) // 2 # xのn桁目が0、yのn桁目が0となるようなnの個数
j = (- a + b + c) // 2 # xのn桁目が0、yのn桁目が1となるようなnの個数
k = (a - b + c) // 2 # xのn桁目が1、yのn桁目が0となるようなnの個数
l = (a + b - c) // 2 # xのn桁目が1、yのn桁目が1となるようなnの個数
x = 0
y = 0
# 上の桁から計算する
for now_digit in range(59, -1, -1):
# input_cのnow_digit桁目が1の場合、xのn桁目かyのn桁目のどちらか一方のみが1である
if (input_c >> now_digit) & 1:
if j:
y |= 1 << now_digit
j -= 1
else:
x |= 1 << now_digit
k -= 1
# input_cのnow_digit桁目が0の場合は、xのn桁目かyのn桁目の両方が0、または両方が1である
else:
if i:
i -= 1
else:
x |= 1 << now_digit
y |= 1 << now_digit
l -= 1
# 出力
print(x, y)

あるいは, ユーザー解説のようにも実装することができます. XYのビットを試しに立ててみて, XYのpopcountが目標数ちょうどに達したかを確かめてみる方法です.

# 入力
a, b, input_c = map(int, input().split())
x = 0
y = 0
# 下の桁から計算する
for now_digit in range(60):
# input_cのnow_digit桁目が1の場合、xのn桁目かyのn桁目のどちらか一方のみが1である
# aとbの余っている方を優先して消費する
if (input_c >> now_digit) & 1:
if a > b:
x |= 1 << now_digit
a -= 1
else:
y |= 1 << now_digit
b -= 1
# 下の桁から計算する
for now_digit in range(60):
# input_cのnow_digit桁目が0の場合、xのn桁目かyのn桁目の両方が0、または両方が1である
# aとbが両方余っている場合のみ1にする
if ((input_c >> now_digit) & 1 == 0) and a > 0 and b > 0:
x |= 1 << now_digit
y |= 1 << now_digit
a -= 1
b -= 1
# 出力
if a == 0 and b == 0:
print(x, y)
else:
print(-1)

E以降

E以降は私の能力不足故に省略いたします.

参考にしたサイト等