そのうち誰かの役に立つ

もしくは誰の役にも立たない

AtCoderチャレンジ供養会場(2020/06/22-2020/06/30)

コンテストでの時間切れや解けなかった過去問を振り返って供養していく

6/22: AtCoder Grand Contest 046 C - Shift

供養

問題

Difficulty: 2084 (記事作成時点)

解法

  •  S 中の 0 の数を  z とすると、問題は  |S| - x 個の 1 z + 1 個の区間に配置した初期状態から、1つ選んでそれが置かれていた区間より左にあるいずれかの区間に再配置するという操作を最大  K 回実施したときの配置のパターン数となる
  •  \mathit{DP}_{i, j, k} i 番目の区間以降で  j 個選び、そのうち  k 個を再配置した状態のパターン数とする
  • 区間  i で行う操作は
    • 何もしない
      •  \mathit{DP}_{i, j, k} = \mathit{DP}_{i + 1, j, k}
    • 1個以上の 1 を再配置する
      •  \mathit{DP}_{i, j, k} = \sum_{l = 0}^{j - 1} \mathit{DP}_{i + 1, j, l}
    • 1個以上の 1 を選ぶ
      •  \mathit{DP}_{i, j, k} = \sum_{l = \mathrm{max}(j - C_{i})}^{j - 1} \mathit{DP}_{i, j, k}
        •  C_{i}区間  i に最初に配置された 1 の数
    • 再配置と選ぶ動作は同時には行わない
  •  \sum \mathit{DP}_{1} が答となる

実装は

  1. 配列  C を用意し、 c = 0 とする
  2.  0 \le i \lt |S| について、下記の操作を実施する
    •  S_{i} = 0 ならば  c C の末尾に追加し  c = 0 とする
    •  S_{i} = 1 ならば  c = c + 1 とする
  3.  c C の末尾に追加する
  4.  C の先頭要素を削除する
  5.  M = |C|, K = \mathrm{min}(\sum C, K) とする
  6.  (M + 1) \times (K + 1) \times (K + 1) のDP表  \mathit{DP} を用意し、 \mathit{DP}_{M, 0, 0} = 1 とする
  7.  i = M - 1, \dots, 0 について、以下の操作を実施する
    •  j = 0, \dots, K について、以下の操作を実施する
      •  c = 0 とする
      •  k = 0, \dots, j について、以下の操作を実施する
        •  c = c + \mathit{DP}_{i + 1, j, k} とする
        •  \mathit{DP}_{i, j, k} = \mathit{DP}_{i, j, k} + c とする
    •  k = 0, \dots, K について、以下の操作を実施する
      •  c = 0 とする
      •  j = k, \dots, K について、以下の操作を実施する
        •  \mathit{DP}_{i, j, k} = \mathit{DP}_{i, j, k} + c とする
        •  c = c + \mathit{DP}_{i + 1, j, k} とする
        •  j \ge C_{i} ならば  c = c - \mathit{DP}_{i + 1, j - C_{i}, k} とする
  8.  \sum \mathit{DP}_{0} を出力する

計算量は  O(N^{3})

公式解説

躓いた点

  • DPを上手く作れなかった

6/24: CODE FESTIVAL 2015 あさぷろ Middle B - ヘイホー君と削除

供養

問題

Difficulty: 1325(estimated) (記事作成時点)

解法

  •  S i 文字目までと  i + 1 文字目以降で分割し、それらの最長共通部分文字列の長さを  L_{i} とすると、 N - 2 \times \mathrm{max}(L_{i}) が答えとなる
  • 最長共通部分文字列はDPで求められる

実装は

  1.  L = 0 とする
  2.  i = 1, \dots, N - 1 について以下の処理を実施する
    •  (i + 1) \times (N - i + 1) のDP表  \mathit{DP} を用意する
    •  j = 0, \dots, i - 1, k = 0, \dots, N - i - 1 について、以下の処理を実施する
      •  \mathit{DP}_{j + 1, k + 1} = \mathrm{max}(\mathit{DP}_{i + 1, j}, \mathit{DP}_{i, j + 1}) とする
      •  S_{j} = S_{i + k} ならば  \mathit{DP}_{i + 1, j + 1} = \mathrm{max}(\mathit{DP}_{i, j} + 1, \mathit{DP}_{i + 1, j + 1}) とする
    •  L = \mathrm{max}(\mathit{DP}_{i, N - i}, L) とする
  3.  N - 2L を出力する

計算量は  O(N^{3})

公式解説

躓いた点

  • 最長共通部分文字列のDPが出てこなかった

6/27: AtCoder Beginner Contest 172 E - NEQ

供養

問題

Difficulty: 1878 (記事作成時点)

解法

  •  1 \le i \lt j \le N である任意の  (i, j) について、 A_{i} \ne A_{j} かつ  B_{i} \ne B_{j} であるという条件だけにする
  • 任意の  S \subseteq \lbrace 1, \dots, N \rbrace について、 i \in S ならば  A_{i} = B_{i} であるような  (A, B) の個数  X_{S} {}_{M}\mathrm{P}_{|S|} \times {}_{M - |S|}\mathrm{P}_{N - |S|} \times {}_{M - |S|}\mathrm{P}_{N - |S|}
    •  N 個のうち  S 個は共通で選び、残りの  N - |S| 個は独立して選ぶ
  • 包除原理より、 \sum_{S \in \mathcal{S}}(-1)^{|S|}X_{S} が求める答えとなる
    •  \mathcal{S} S のとりうる集合の集合
  •  (-1)^{|S|}X_{S} |S| にのみ依存するので、 |S| が等しくなる項をまとめると  \sum_{k = 0}^{N} {}_{N}\mathrm{C}_{k} \times (-1)^{k} \times {}_{M}\mathrm{P}_{k} \times ( {}_{M - k}\mathrm{P}_{N - k} )^{2} で計算できる

実装は

  1.  0 \le k \le M について、 F_{k}, G_{k}, I_{k} でそれぞれ  k!, k!^{-1}, k^{-1} を求めておく
  2. 長さ  N + 1 の配列  P を用意し、 P_{0} = 1 とする
  3.  i = 1, \dots, M について、 P_{i} = P_{i - 1} \times (M - i + 1) とする
  4. 長さ  N + 1 の配列  Q を用意し、 Q_{0} = P_{N} とする
  5.  i = 1, \dots, N について、 Q_{i} = Q_{i - 1} \times I_{M - i + 1} とする
  6.  A = 0, s = 1 とする
  7.  0 \le k \le N について、以下の操作を実施する
    •  A = A + s \times {}_{N}\mathrm{C}_{k} \times P_{k} \times Q_{k} \times Q_{k} とする
    •  s = -s とする
  8.  A を出力する

計算量は  O(M + N)

公式解説

躓いた点

  • 包除原理が使えることに思い至らなかった

6/28: AtCoder Beginner Contest 172 F - Unfair Nim

供養

問題

Difficulty: 2159 (記事作成時点)

解法

  •  X = A_{3} \oplus \dots \oplus A_{N} としたとき、 (A_{1} - d) \oplus (A_{2} + d) = X であるような  0 \ge d \lt A_{i} の最小値を求める
  •  A_{1} + A_{2} = (A_{1} - d) \oplus (A_{2} + d) + 2 \times ( (A_{1} - d)\;\mathrm{AND}\;(A_{2} + d) ) と表せる
  •  S = A_{1} + A_{2}, D = (A_{1} - d)\;\mathrm{AND}\;(A_{2} + d) としたとき、上式は(解が存在すれば)  S = X + 2D と表せるので、 D = \frac{S - X}{2} と表記できる
    •  S - X \lt 0 もしくは  (S - X) \% 2 = 1 のときは解なし
    • また、 D X に共通で立っているbitがあるときも解なしとなる
  •  X のbitを2つに分けた  Y, Z (i.e.  Y + Z = X, Y \oplus Z = X, Y\;\mathrm{AND}\;Z = 0)を考えると、 A_{1} - d = Y + D, A_{2} = Z + D と表せる
  •  X の上位bitから見ていって、 Y + D \lt A_{1} となるように  Y を決めていけばよい

実装は

  1.  X = 0 とし、 2 \le i \lt N について  X = X \oplus A_{i} とする
  2.  S = A_{0} + A_{1} とし、 S \gt X もしくは  (S - X) \% 2 = 1 ならば -1 を出力して終了
  3.  D = \frac{S - X}{2} とし、 D\;\mathrm{AND}\;X \ne 0 ならば -1 を出力して終了
  4. 配列  B を用意し、 x = X とする
  5.  x = 0 となるまで以下の操作を繰り返す
    • b = x\;\mathrm{AND}\;(-x) とし、 x = x - b とする
    •  b B の末尾に追加する
  6.  i = |B| - 1, \dots, 0 について、 D + B_{i} \le A_{0} ならば  D = D + B_{i} としていく
  7.  D = 0 もしくは  D \gt A_{0} ならば -1 を出力して終了
  8.  A_{0} - D を出力する

計算量は  O(N + \mathrm{log}X)

公式解説

躓いた点

  • 加算とbit演算の関係が思いつかなかった