Atcoder - ABC341 A - E まで復習解説
ABC341 A - E を復習してまとめてみました。初学者の方の参考になれば幸いです。
A - Print 341
問題文
正整数が与えられるので、 N 個の N と 0 個の N+1 からなる、 1 と 0 が交互に並んだ文字列を出力してください。 1
Python の場合文字列を "abc" * 3
のようにかけ算の式をあたえると "abcabcabc"
のように 3 回連続することができます。今回の問題の場合は
(逆に
N = int(input())
ans = "10" * (N + 1)
ans = ans[:-1]
print(ans)
B - Foreign Exchange
最初読んだ時に実世界同様すべての通貨の間で変換可能と勘違いして、めちゃくちゃ難しくない?と思ってしまいましたが、国
であれば、いわゆる貪欲法として国
N = int(input())
A = list(map(int, input().split()))
for i in range(N - 1):
s, t = map(int, input().split())
div, mod = divmod(A[i], s)
A[i] = mod
A[i + 1] += t * div
print(A[-1])
C - Takahashi Gets Lost
(とはいいつつ、私はコンテスト中 Python で提出した後 TLE になってしまい「
H, W, N = map(int, input().split())
T = list(input())
S = [list(input()) for _ in range(H)]
def f(h, w):
# h, w から移動していってすべて . なら 1 を返す
i, j = h, w
for t in T:
match t:
case "L":
j -= 1
case "R":
j += 1
case "U":
i -= 1
case "D":
i += 1
if S[i][j] == "#":
return 0
return 1
ans = 0
for h in range(H):
for w in range(W):
if S[h][w] == ".":
ans += f(h, w)
print(ans)
D - Only one of two
「一方のみの倍数」や「両方の倍数」を満たすような個数を求める問題は包除原理を用いることが多いです。
今回求めたい「値
と表すことができます。(最後の項に
あとは
そこで倍数の数は単調増加していくことを利用して「値 True
そうでなければ False
を返す」関数
ちなみに私はなまじ入力例の
N, M, K = map(int, input().split())
def f(x):
# 整数 x までで (N でのみ割れるもの + M でのみ割れるもの) の個数が K 個以上あるか
lcm_ = lcm(N, M)
return x // N + x // M - x // lcm_ * 2 >= K
ng = -1
ok = 10 ** 60
while ok - ng > 1:
mid = (ok + ng) // 2
if f(mid):
ok = mid
else:
ng = mid
print(ok)
E - Alternating String
高速に区間を更新する必要があるので前回同様 Lazy Segment Tree で XOR を管理するのかなーと最初思ってしまいコンテスト本番では解けませんでした。以下、解答 AC です。
まず反転させることで 良い文字列 であるかどうかがどう変わっているか考えます。
(コンテストではこの辺の考察が抜けていたので次回は頑張りたいです)
例えば、
次に
- 前から5番目から8番目を反転する
→10\textcolor{red}{1}1[0101]0 10\textcolor{red}{11}[101\textcolor{red}{0}]0 - 前から2番目から6番目を反転する
→1[0\textcolor{red}{1}101]010 \textcolor{red}{1}[1\textcolor{red}{0}01\textcolor{red}{0}]010 - 前から4番目のみ6番目を反転する
→10\textcolor{red}{1}[101]010 101[01\textcolor{red}{0}]010
こうしてみると、赤かったのが黒になったり新たに黒が赤くなっているのがカッコの前のみということから
- 反転する先頭
がもともと良かったら悪く、悪かったら良くなるl - 同様に末尾
がもともと良かったら悪く、悪かったら良くなるr - その間は元の状態から全部一斉に反転するので変化しない
ということがわかります。
そこで、そもそも
- クエリ
の場合:1 ,A[l - 1] の要素を 0 なら 1 に、 1 なら 0 にする (A[r] なのは上の実験で言うカッコの前に合わせるためです)l - 1 - クエリ
の場合:区間2 の間に一つもA[l] - A[r] がなければ 良い文字列 とする1
とすれば OK です。一点更新、区間取得なので Lazy Segment Tree ではなく Segment Tree でも十分実装が可能です。
Python の AtCoder 環境は公式の C++ ライブラリを Python に移植していただいている https://github.com/not522/ac-library-python がインストールされていますので Segment Tree を import して使えます。
from atcoder.segtree import SegTree
def op(a, b):
return a + b
e = 0
N, Q = map(int, input().split())
S = list(map(int, list(input())))
# 1-indexed してたほうが計算が簡単です
T = [0] * (N + 1)
for i in range(1, N):
if S[i - 1] == S[i]:
T[i] = 1
segtree = SegTree(op, e, T)
for _ in range(Q):
t, l, r = map(int, input().split())
match t:
case 1:
l -= 1
segtree.set(l, segtree.get(l) ^ 1)
segtree.set(r, segtree.get(r) ^ 1)
case 2:
ans = "Yes" if segtree.prod(l, r) == 0 else "No"
print(ans)
Discussion