そのうち誰かの役に立つ

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

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

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

6/1: AtCoder Regular Contest 039 D - 旅行会社高橋君

供養

問題

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

解法

  • グラフを二辺連結成分に分解する
    • DFSをしながら各点  v の深さ  D_{v} を求めていく
    • DFSの探索木に含まれない辺を後退辺とする
      •  uv が後退辺のとき、 |D_{u} - D_{v}| \ge 2 となる
    • 各点から深さを増加させる方向の辺を任意の回数、後退辺を1回以下使用して到達できる最小の深さ(low-link)を  L_{v} とする
      • 自身および自身の親以外の隣接点の深さの最小値を覚えておき、葉から根方向へボトムアップに最小値を更新していけば求めることができる
    • DFSの探索木に含まれる辺  uv について、 u を親としたときに  D_{u} \gt L_{v} ならば  uv は橋(bridge)である
      • bridgeを取り除くとグラフは連結でなくなる
    • グラフからbridgeを全て取り除いたときに残ったそれぞれの連結成分は二辺連結成分である
  • 各二辺連結成分の代表点とbridgeからなるグラフ  H を考える
    •  G の各点  v について、 v が属する二辺連結成分の代表点に対応する  H 上の点を  V_{v} とする
    • bridge  uv V_{u} V_{v} を結ぶとする
    • このとき、 H は木となる
  • 重連結成分の任意の3点はそれらを含む辺素なサイクルを構成できるので、 A_{i}, B_{i}, C_{i} が同一二辺連結成分に属していれば OK となる
  •  A_{i} B_{i} が同一二辺連結成分に属していれば、 A_{i} から  B_{i} を経由して2点が属する二辺連結成分の出口、[tec: C_{i}] が属する二辺連結成分の入口、 C_{i} と移動していくことができる
    •  B_{i} C_{i} が同一二辺連結成分に含まれている場合も同様
  •  A_{i} B_{i} が属する二辺連結成分がそれぞれ異なり、 B_{i} C_{i} が属する二辺連結成分がそれぞれ異なるとき、 H 上で  V_{A_{i}} から  V_{C_{i}} へのパス上に  V_{B_{i}} がいるかで判定する
    •  V_{A_{i}}, V_{B_{i}} 間の距離  + V_{B_{i}}, V_{B_{i}} 間の距離  = V_{A_{i}}, V_{C_{i}} 間の距離であるかで判定できる
    • 2点  u, v 間の距離は  u の深さ  + v の深さ  - 2 \times \mathit{LCM}(u, v) の深さで計算できる
  • 同一の二辺連結成分に属する場合は上記の距離は0となるので、

実装は

  1. 長さ  N の配列  D, L, P および配列  F を用意する
  2. グラフを点0からDFSし、点  v の深さを  D_{v} v から伸びる後退辺の先の深さの最小値を  L_{v} v の親を  P_{v}、発見順を  F に記録していく
    • 後退辺が存在しない点については  L_{v} = D_{v} とする
  3.  F の逆順に見ていき、現在見ている点  v について  L_{P_{v}} = \mathrm{min}(L_{v}, L_{P_{v}}) と更新していく
  4. Union-Findのための関数  \mathit{Unite}(u, v), \mathit{Find}(u) を用意する
  5. 配列  B を用意する
  6.  0 \le i \lt M について、以下の操作を実施する
    •  D_{x_{i}} \gt D_{y_{i}} ならば  x_{i} y_{i} を入れ換える
    •  D_{y_{i}} - D_{x_{i}} \ge 2 ならば後退辺なので何もせず次へ行く
    •  D_{v} \ge L_{y_{i}} ならば  \mathit{Unite}(x_{i}, y_{i}) をする
    •  D_{v} \ge L_{y_{i}} ならば  i B の末尾に追加する
  7.  N 個の独立点からなるグラフ  H を用意する
  8.  B の各要素  i について、 H 上の点  \mathit{Find}(x_{i}) \mathit{Find}(y_{i}) を接続する
  9.  H を0から探索し、各点の深さ  D^{'}_{v} を求める
  10.  H 上の任意の2点  u, vLCAを求める関数  \mathit{LCA}(u, v) を用意する
  11.  H 上の任意の2点  u, v の距離を求める関数  \mathit{Dist}(u, v) を以下のように用意する
    •  w = \mathit{LCA}(u, v) とし、 D^{'}_{u} + D^{'}_{v} - 2D^{'}_{w} を返す
  12.  0 \le i \lt Q について以下の操作を実施する
    •  a = \mathit{Find}(a_{i}), b = \mathit{Find}(b_{i}), c = \mathit{Find}(c_{i}) とする
    •  \mathit{Dist}(a, b) + \mathit{Dist}(b, c) = \mathit{Dist}(a, c) ならば OK を出力し、そうでなければ NG を出力する

計算量は前処理が  O(N + M)、クエリごとに  O(\mathrm{log}N) なので全体で  O(N + M + Q\mathrm{log}N)

公式解説

躓いた点

  • 二辺連結成分分解すればいいという発想が出なかった

6/2: AtCoder Beginner Contest 137 F - Polynomial Construction

供養

問題

Difficulty: 2435 (記事作成時点)

解法

  • Fermatの小定理より  a \% p \neq 0 について  a^{p - 1} = 1 から、 1 - (x - i)^{p - 1} x = i ならば1、そうでなければ0
  • 上記の式がすべての  A_{i} = 1 である  i のときに成立すればよいので、求める式は  \sum_{i | A_{i} = 1}\bigl(1 - (x - i)^{p - 1}\bigr)

実装は

  1. 長さ  p の配列  B を用意し、 0 \le i \lt p について  B_{i} = _{p - 1}\mathrm{C}_{i} とする
    • 事前に  0 \le k \lt p について  k!, k!^{-1} を求めておく
  2.  p \times p の配列  C を用意し、 C_{i, j} = (-i)^{j} とする
  3. 長さ  p の配列  D を用意する
  4.  0 \le i \lt p について以下の操作を実施する
    •  D_{0} A_{i} を加算する
    •  0 \le j \lt p について、 D_{j} にから  A_{i} \times B_{j} \times C_{i, p - 1 - j} を減算する
  5.  D を出力する

計算量は  O(p^{2})

公式解説

躓いた点

  • 愚直に掃き出し法をやってTLE
  • 式変換が思いつかなかった

6/5: DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選 C - アメージングな文字列は、きみが作る!

供養

問題

Difficulty: 2443 (記事作成時点)

解法

  •  Sa 以外の文字が  K 個以下しかないとき、 |S| - K 個の a にするのが辞書順最小
  • そうでないとき、先頭から連続する a の長さを最大化した上で、残りが辞書順最小になるようにするのが辞書順最小
  • 先頭から見て a でない文字が  K + 1 個登場するまでに登場した a の集合を  A A で最後に登場した位置を  j とする
  •  i \lt ja でない文字  s_{i}a に置換することで先頭から連続する a の長さを最大化できる
  •  s_{j} 以降については先頭に a を挿入しても置換しても先頭から連続する a の長さは  K + |A| で変わらないので、後に続く文字列が辞書順最小になるよう置換する文字数を決める

実装は

  1. 配列  T を用意し、 a = 0, j = 0 とする
  2.  0 \le i \lt |S| について、以下の処理を実施する
    •  |T| \le K \land s_{i} = a のとき、 a = a + 1, j = i とする
    •  s_{i} \ne a のとき、 T の末尾に  i を追加する
  3.  |T| \le K のとき、a |S| - K 文字連続させた文字列  S^{'} を出力して終了
  4. a K + a 文字連続させた文字列  S^{'} を作る
  5.  SSuffix Array  \mathit{A} を作る
  6.  i = 0, \dots について以下の処理を実施する
    •  j \lt A_{i} \le T_{K} ならば  S^{'} + S_{A_{i}\dots} を出力して終了
      •  S_{A_{i}\dots} S の(0-indexedで)  A_{i} 文字目以降

計算量は  O(|S|)

公式解説

躓いた点

  • a の最大化の方法を間違えた

6/6: AtCoder Regular Contest 038 C - 茶碗と豆

供養

問題

Difficulty: 2445 (記事作成時点)

解法

  • この問題は個数  i の山が  A_{i} 個あるNimである
  • 同じ山が  A_{i} 個あっても、勝敗に影響するのは  A_{i} % 2
  •  i についてGrundy数を求めればよい
    •  j \lt i についてGrundy g が最後に登場した  j をSegment Treeの  g の要素で覚えておく
    •  \lbrack 0, g) の範囲の最小値が  i - C_{i} 以上ならば  iGrundy数は  g 以上である
    •  g について二分探索して求めていく

実装は

  1.  N 要素のSegmnt Tree  T を用意し、 T_{0} = 0, T_{i \ne 0} = N とする
    •  \mathit{Get}(g) で \lbrack T_{0}, \dots, T_{g} ) の範囲の最小値を返す
    •  \mathit{Update}(i, x) T_{i} = x に更新する
  2. Grundy数を求める関数  \mathit{Grundy}(i, c, m) を以下のように作る
    •  \mathit{Get}(1) \lt i - c ならば  0 を返す
    •  \mathit{Get}(m + 1) \ge i - c ならば  m +1 を返す
    •  0 \le g \lt m + 1 の範囲で  \mathit{Get}(g) \ge i - c となる最大の  g を二分探索で求めて返す
  3.  m = 0 とする
  4. 長さ  N の配列  G を用意する
  5.  i = 1, \dots, N - 1 について以下の処理を実施する
    •  G_{i} = \mathit{Grundy}(i, C_{i}, m) とする
    •  \mathit{Update}(G_{i}, i) を実施する
    •  m = \mathrm{max}(G_{i}, m) とする
  6.  X = 0 とする
  7.  0 \le i \lt N について  X = X \oplus (G_{i} \times (A_{i} \% 2)) とする
  8.  X \ne 0 ならば First、そうでなければ Second を出力する

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

公式解説

躓いた点

  • Grundy数を高速に求める方法を思いつかずにTLE

6/7: AtCoder Regular Contest 024 D - バス停

供養

問題

Difficulty: 2447 (記事作成時点)

解法

  •  x を固定して  (x, y_{i}) にバス停を設置すると、 x_{i} \le x \le x_{j} であるような  i, j はマンハッタン距離での移動ができる
  •  x_{i}, x_{j} がともに  x より小さい、もしくはともに  x より大きい場合は  (x, y_{i}), (x, y_{j}) を使った移動がマンハッタン距離ではないので、それぞれの集合について再帰的にバス停の配置をしていく
  • バス停の座標を管理して重複を排除する

実装は

  1. 連想配列  D を用意して  D_{x, y} に値があれば  (x, y) にバス停があるとする
  2.  0 \le i \lt N について D_{x_{i}, y_{i}} に値をセットする
  3.  (x_{i}, y_{i}) x_{i} についてソートする
  4. 空の配列  B を用意する
  5. 以下の関数  F(l, r) を用意する
    •  l = r ならそのまま返す
    •  m = \frac{l + r}{2} とする
    •  F(l, m), F(m + 1, r) を実施する
    •  l \le i \le r について、以下の処理を実施する
      •  D_{x_{m}, y_{i}} に値があれば何もせず次に行く
      •  B (x_{m}, y_{i}) を追加する
      •  D_{x_{m}, y_{i}} に値をセットする
  6.  F(0, N - 1) を実行する
  7.  |B| を出力する
  8.  B の各要素を出力していく

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

公式解説

躓いた点

  • 分割統治で設置していく発想が無かった