Processing math: 100%

M-SOLUTIONS プロコンオープン2021(AtCoder Beginner Contest 232) E,G,H問題メモ

E - Rook Path

問題

  • H×W の盤面の (X1,Y1) にルーク(飛車)がいる
  • 列または行が同じマス(列も行も同じマスは除く)に移動させることを繰り返す
  • ちょうど K 回の移動後、(X2,Y2) にいるような動かし方の個数を mod998244353 で求めよ
  • 2H,W109
  • 1K106

解法

壁マスとかはないので移動には対称性があり、複数の行・列は一絡げにまとめて考えられる。

今どこにいようと、1回で移動可能なマスは (H1)+(W1) 通りなので、 例えば最終的にどこにいてもいい問題なら、単に (H+W2)K 通りとなる。

ただしこの問題では最終的には (X2,Y2) にいるかどうかが区別できないといけない。

よって、以下の4つの「状態」に分けておけばよい。

  • (X2,Y2) にいる
  • (X2,Y2) にいる
  • (X2,Y2) にいる
  • (X2,Y2) にいる

同じマスには移動できないことを踏まえて①~④の間の遷移を考えると、こんな感じになる。

移動元\先         ①   ②   ③  ④
    ①    (H-2)+(W-2)   1    1   0
    ②           W-1  H-2    0   1
    ③           H-1    0  W-2   1
    ④             0  H-1  W-1   0

①~④をグラフの頂点と見なして、 移動元から移動先の頂点にそれぞれ上表の数だけ有向辺が張ったグラフを考える。
ルークを1回動かすことは、辺に沿って頂点を1回移動することと言い換えることができる。

上の表を隣接行列 T とすると、K 回の移動方法の個数は、TK で表現できる。

行列累乗で O(logK)TK を求めた後、 (X1,Y1) が①~④のどれに当たるかによって、それ→④への経路数が答え。

Python3

G - Modulo Shortest Path

問題

  • N 個の正整数列 (A1,...,AN)(B1,...,BN) と正整数 M が与えられる
  • N 頂点の有向完全グラフを考える
  • ij のコストは、(Ai+Bj)M で割ったあまりである
  • 頂点 1 から N への最短距離を求めよ
  • 2N2×105
  • 2M109
  • 0Ai,Bi<M

解法

愚直にやると O(N2) 本の辺ができてしまうので無理。辺のコストが規則的なことを使うしかない。

Ai+Bj2M を超えることはないので、

  • M を超えたら Ai+BjM
  • M を超えないなら Ai+Bj

となる。Ai を固定すると、BjMAi 以上かどうかが分水嶺となる。

なので、まぁ類題を解いたことがないとなかなかひらめくのは難しい気がするが、 頂点を A 側と B 側の2種類に分けた上で、

サンプル1
Ai  10  11   6   0
 i  ①  ②  ③  ④
 
 j  ④→③→②→①
Bj   1   4   7   8

Bj を昇順に並び替え、小さい順に次の頂点へ辺を張っていく。

A 側の頂点 As から、和が M を超える最小の B 側の頂点 Bt へと辺を張ると、 Bt から移動できる B 側の頂点は (自身より大きい頂点にしか移動できないので)全て As との和が M 以上となる。

なので、

  • AsBt に、コスト As+BtM の辺を張る
  • As(B) に、その和のコストの辺を張る

としてやると、相手によってコストが変化する問題を上手く表現できる。

そして、BA のように A 側の同じ頂点に戻る辺も張ってやると、 その辺を使って戻った頂点が、元のグラフで実際に次に移動する頂点となる。

(※オレンジ線の真ん中のコストは3の間違い)

ただし、Ai によっては青の辺は存在しないこともある。

このグラフでDijkstraをすれば、2N 頂点 4N 辺なので、間に合う。

Python3

H - King's Tour

問題

  • H×W の盤面の左上 (1,1) に、周囲8マスに動けるキング(王将)がいる
  • 全てのマスを1回ずつ経由して (a,b) で移動を終えるようなルートを1つ構築せよ
  • 2H,W100

解法

E問題に続きチェス問題。

時間をかければひたすら場合分けすることで気合いで通すことはできそうだが、いかに速く実装できるか、が本質と見た方がよさそう。

周囲8マスに移動できるのは自由度が高くて、適当に移動して多少変な形に残ったとしても、(完全に分断されなければ)なんなりと埋めることはできそう。

貪欲を試してみる。

  • 最初に適当に、(1,1)(a,b) への(全てのマスを通過しなくてもよい)パスを作る
  • パス中にマス C1C2 に移動する瞬間があったとして、その両方に隣接するマス C3 がまだ未経由なら、C1C3C2 に張り替える
  • 全てのマスを経由するまで上を繰り返す

やってみると、サイズが小さいときにWAが出た。

どうも、幅が 23 マスで細長い時にゴールより向こう側に移動できなかったり、a=2,b=W などの時に隅に1マスだけ余りがちらしい。

上手くいくかどうかは、最初の (1,1)(a,b) へのパスの作り方、複数の未経由の C3 があったときにどれを優先するか、によっても変わってくる。

途中で不可能に陥ったら一旦破棄して、最初のパスを決める際にまず (H,W) まで移動して、そこから (a,b) に戻るものを初期状態とすることで、上手くいった。
必ず上手くいく証明はしてない。

やってることは単純なはずなのに、できあがる経路は結構キモい。

未経由の C3 が複数あったときの優先順位を変えるとガラッと変わって綺麗になったりして面白い。

Python3

programming_algorithm/contest_history/atcoder/2021/1219_abc232.txt · 最終更新: 2021/12/27 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0