黒ココアさんのメモ置き場

メモを置いたり置かなかったり

ABC345 D - Tiling

atcoder.jp

問題

  • $H \times W$のマス目と、$N$枚のタイルが与えられるよ
    • $i$枚目のタイルは、$ A_i \times B_i $の長方形だよ
  • タイルを使って、マス目をピッタリ埋める事ができる?
    • 重なったり、空白があったりしちゃだめだよ
    • タイルは回転させてもいいよ

成約

  • $1 \leq N \leq 7$
  • $1 \leq H,W \leq 10$
  • $1 \leq A_i, B_i \leq 10$

思考

  • 制約が超絶小さい…ので、ゴリ押し力技の全探索
    • 一旦計算量を考えてみる
      • どのタイルを使う?→bit全探索で$O(2^{N})$
      • 使う予定のタイルの、回転どうする?→bit全探索で$O(2^{N})$
      • 使う予定のタイルの、順番どうする?→並べ替えで$O(N!)$
      • 決めたタイルの使い方、手順を、左上から順に機械的に埋めていく
        • もし全タイル丁度埋めきれたら、OK!→判定に$O(H*W)$
  • いくら$N,H,W$が小さいとはいえ…これは…
    • $2^{N} \times 2^{N} \times N! \times H \times W$
    • $=8,257,536,000$…これは、流石に、無理…
      • ところがどっこい通るんですねぇ…
    • タイルを何枚使うかを、小分けにして考えてみる
      • 1枚使うパターン:7通り
        • $7\times2^{1}\times 1! \times H \times W = 14HW$
      • 2枚使うパターン:7C2=21通り
        • $21 \times 2^{2} \times 2! \times H \times W = 168HW$$
      • 3枚使うパターン:7C3=35通り
        • $35 \times 2^{3}\times 3! \times H \times W = 1,680HW$$
      • 4枚使うパターン:7C4=35通り
        • $35 \times 2^{4} \times 4! \times H \times W = 13,440HW$
      • 5枚使うパターン:7C5=21通り
        • $21 \times 2^{5} \times 5! \times H \times W = 80,640HW$
      • 6枚使うパターン:7C6=7通り
        • $7 \times 2^{6} \times 6! \times H \times W = 322,560HW$
      • 7枚使うパターン:7C6=1通り
        • $1 \times 2^{7} \times 7! \times H \times W = 645,120HW$
    • $H,W$は固定なので、上記パターンに分類される
      • 一番重い、7枚使うパターンを、7回回すっていう感覚で、
        • $7 \times 645,120 \times10\times 10 = 451,584,000$
        • $8,257,536,000$から比べると一気に小さくなった
    • 計算量の見積もり、とても、難しい…

コード

import itertools

N, H, W = map(int, input().split())
tiles = []
for i in range(N):
    A, B = map(int, input().split())
    tiles.append([A, B])

master = [[1] * W for _ in range(H)]


def calc(tiles):
    base = [[0] * W for _ in range(H)]
    cur = 0
    for i in range(H):
        for j in range(W):
            if len(tiles) == cur: break
            if base[i][j] != 0: continue
            tile_x, tile_y = tiles[cur]
            if i + tile_x > H or j + tile_y > W: return False
            for x in range(i, i + tile_x):
                for y in range(j, j + tile_y):
                    if base[x][y] != 0: return False
                    base[x][y] = 1
            cur += 1
    if base == master:
        return True
    return False


for saiyou in range(1, 1 << N):
    saiyou_tiles = []
    area_sum = 0
    for i in range(N):
        if saiyou >> i & 1:
            saiyou_tiles.append(tiles[i])
            area_sum += tiles[i][0] * tiles[i][1]

    tile_cnt = len(saiyou_tiles)
    for omoteura in range(1 << tile_cnt):
        omoteura_tiles = []
        for i in range(tile_cnt):
            if omoteura >> i & 1:
                omoteura_tiles.append(saiyou_tiles[i])
            else:
                omoteura_tiles.append([saiyou_tiles[i][1], saiyou_tiles[i][0]])

        for junban in itertools.permutations(omoteura_tiles):
            if calc(junban):
                print('Yes')
                exit()
print('No')

ABC345 C - One Time Swap

atcoder.jp

問題

  • 文字列$S$が与えられるよ
  • 次の操作を1回行った後の文字列として、有り得るものはいくつある?
    • Sのうち2つの文字を選んで、入れ替える

成約

  • $2 \leq |S| \leq 10^{6}$

思考

  • $O(N^{2})$は通らない…
  • 片方を全探索して、もう片方がサクサクッと求められたら嬉しいね
    • 全探索の典型
    • 手順:
      • 事前準備として、全アルファベットがいくつある?を記録(Counter)
      • 先頭から順に一文字ずつ確認(全探索)
        • Counterから、その1文字分の集計を消す
          • Counterに記録されているのは、「今見ている文字」から右にある文字ごとの数!
        • a~zまでの26文字、順番に数を数えていく
          • 「今見ている文字」と同じ文字の個数に関して取り扱い注意!
  • そもそも英小文字=26種類しかないため、文字を2つどう選んでも、$26\times26$しかない
    • aとbを入れ替えるパターンはいくつ?
    • aとcを入れ替えるパターンはいくつ?
    • bとcを入れ替えるパターンはいくつ?
  • こっちで集計したほうが明らかに速いですね!

コード(解1)

from string import ascii_lowercase
from collections import Counter

S = input()
c = Counter(S)

ans = 0
same = 0
for from_c in S:
    c[from_c] -= 1
    for to_c in ascii_lowercase:
        if from_c != to_c:
            ans += c[to_c]
        elif c[to_c] > 0:
            same += 1

if same:
    ans += 1

print(ans)

コード(解2)

from string import ascii_lowercase
from collections import  Counter

S = input()
c = Counter(S)

ans = 0
same = 0
for a in ascii_lowercase:
    for b in ascii_lowercase:
        if a == b:
            if c[a] > 1:
                same += 1
        else:
            ans += c[a] * c[b]

ans //= 2
if same:
    ans += 1

print(ans)

ABC345 B - Integer Division Returns

atcoder.jp

問題

  • 整数$X$が与えられるよ
  • $ \lceil \frac{x}{10} \rceil $を出力してね

成約

  • $-10^{18} \leq X \leq 10^{18}$

思考

  • math.ceilで行けるでしょwww
    • 誤差で通らず無事致命傷
  • ちゃんと切り上げ除算、関数で準備しておきませう、というTips

コード

X = int(input())


def div_ceil(a, b):
    return (a + b - 1) // b


print(div_ceil(X, 10))

ABC345 A - Leftrightarrow

atcoder.jp

問題

  • "<"、"="、">"で作られた文字列$S$が与えられるよ
  • $S$が、双方向矢印型かどうか判定してね

成約

  • $3 \leq |S| \leq 100$

思考

  • 最初が"<"、最後が">"、それ以外が"="かどうかを判定すれば良いね

コード

S = input()

if S[0] == "<" and S[-1] == ">" and S.count("=") == len(S)-2:
    print('Yes')
else:
    print('No')

ABC344 E - Insert or Erase

atcoder.jp

問題

  • 長さ$N$の数列$A$が与えられるよ
  • Q個のクエリを順番に処理してね
    • クエリ1:$A$の中の要素$x$の直後に$y$を挿入してね
    • クエリ2:$A$の中にある要素$x$を削除してね
  • 全部のクエリを捌いた後の$A$はどうなってる?

成約

  • $1 \leq N \leq 2 \times 10^{5}$
  • $1 \leq Q \leq 2 \times 10^{5}$
  • $A$の中の要素は常にユニーク
  • クエリが与えられた時、$A$の中に$x$は絶対存在するよ

思考

  • 電車の類題(ABC225D)を思い出す
  • 特定の要素の、「前の数字は何?」と、「後ろの数字は何?」を管理・更新していくと良さそう
    • クエリ1の捌き方:
      • 「$x$の後ろの数字」をメモ(tmp)
      • 「$y$の前の数字」を$x$に更新(新規作成)
      • 「$y$の後ろの数字」を「$x$の後ろだった数字(tmp)」に更新(新規作成)
      • 「$x$の後ろの数字」を$y$に更新
      • 「$x$の後ろだった数字(tmp)の、前の数字」を、$y$に更新
    • クエリ2の捌き方:
      • 「$x$の後ろの数字」を、「$x$の後ろの後ろの数字」に更新
      • 「$x$の前の前の数字」を、$x$に更新
  • 本番は$N=1$のケースでめっちゃしばかれました(5ペナ)
    • tips: 番兵として、先頭と末尾の車両を固定で置いておくと幸せになれました

コード

N = int(input())
A_L = [0] + list(map(int, input().split())) + [-1]

link_d = dict()
for i in range(1, N + 1):
    link_d[A_L[i]] = [A_L[i - 1], A_L[i + 1]]
link_d[0] = [None, A_L[1]]
link_d[-1] = [A_L[N], None]

Q = int(input())

for _ in range(Q):
    query = list(map(int, input().split()))

    if query[0] == 1:
        prev_num, add_num = query[1:]
        next_num = link_d[prev_num][1]

        link_d[prev_num][1] = add_num
        link_d[add_num] = [prev_num, next_num]
        link_d[next_num][0] = add_num

    else:
        del_num = query[1]
        prev_num, next_num = link_d[del_num]

        link_d[prev_num][1] = next_num
        link_d[next_num][0] = prev_num

ans_L = []
now_num = 0

while True:
    ans_L.append(now_num)
    if link_d[now_num][1] == -1:
        break
    now_num = link_d[now_num][1]
print(*ans_L[1:])

ABC344 D - String Bags

atcoder.jp

問題

  • 最初に文字列$T$と、空文字列$S$を持ってるよ
  • 文字列がいくつか入った袋が$N$個あるよ
    • 袋$i$には、$A_i$個の文字列が入ってるよ
  • 以下の手順を$i=1,2,...,N$について繰り返すよ
    • 1円を払って、袋$i$から好きな文字列を1つ選んで、文字列$S$の末尾に連結する
    • 何もしない
  • 最終的に文字列$S$と文字列$T$が一致する?
    • 一致するなら、必要な最小の金額を教えてね!

成約

  • $1 \leq |T| \leq 100$
  • $1 \leq N \leq 100$
  • $1 \leq A_{i} \leq 100$
  • $1 \leq S_{i,j} \leq 10$

思考

  • 雰囲気から、DP!
    • 問題文に「DはDPのD」って書いてる
  • 初期値:「空文字列」を作るための最小コスト=0
  • 遷移:
    • 「今作れてる文字列」に、新しい袋から文字列を1つ取り出して、引っ付けてみる
    • もしTの先頭と一致しているなら、とりあえず採用!
      • コストの最小値を見ていく
  • 丁寧にindex(何文字目まで作成できた!)で見ていったほうが良いんだろうなぁ、と思ってはいる…
    • が、T.startswith()で接頭語だけに絞れば、割と余裕で通ったので良かった
    • 文字列の長さがどれも短かったから…?
      • 正直、Tが$10^{6}$みたいな長さしてたらTLE頂いてた可能性は十分有り
      • そのうちindexでちゃんと書き換える :TODO

コード

T = input()
N = int(input())
words = []
for i in range(N):
    tmp = input().split()
    words.append(tmp[1:])

now_s = set()
now_s.add("")
cost_d = dict()
cost_d[""] = 0

for i in range(N):
    next_s = set()
    for now_word in now_s:
        next_s.add(now_word)

        for add_word in words[i]:
            tmp = now_word + add_word
            if T.startswith(tmp):
                next_s.add(tmp)
                if tmp in cost_d.keys():
                    cost_d[tmp] = min(cost_d[tmp], cost_d[now_word] + 1)
                else:
                    cost_d[tmp] = cost_d[now_word] + 1

    now_s = next_s
print(cost_d[T] if T in cost_d.keys() else -1)

ABC344 C - A+B+C

atcoder.jp

問題

  • 3個の数列$A,B,C$が与えられるよ
  • 長さ$Q$の数列$X$も与えられるよ
  • 各$i=1,...,Q$について、以下の問題に答えてね
    • $A,B,C$から要素を1つずつ選んで、和を$X_{i}$にすることができる?

成約

  • $1 \leq |A|,|B|,|C| \leq 100$
  • $0 \leq A_i, B_i ,C_i \leq 10^{8}$
  • $1 \leq Q \leq 2 \times 10^{5}$
  • $0 \leq X_{i} \leq 3\times 10^{8}$

思考

  • $A,B,C$はそんなに長くないですね
    • (Aの要素1つ)+(Bの要素1つ)+(Cの要素1つ)のパターンは、$100^{3}$個しか無いね!
    • 前準備として、先に要素を1つずつ選んだ和として作れるものを全部列挙しておくと、$X$の質問にすぐ答えられるね!

コード

N = int(input())
A_L = list(map(int, input().split()))
M = int(input())
B_L = list(map(int, input().split()))
L = int(input())
C_L = list(map(int, input().split()))
Q = int(input())
X_L = list(map(int, input().split()))

sum_s = set()
for a in A_L:
    for b in B_L:
        for c in C_L:
            sum_s.add(a + b + c)

for x in X_L:
    if x in sum_s:
        print("Yes")
    else:
        print("No")