目次

AtCoder Regular Contest 179 B,D問題メモ

AtCoder Regular Contest 179

B - Between B and B

B - Between B and B

問題文

制約

解法

たとえば $X_1=5$ のとき、一度 “1” を置いたら、“5” を置くまで、次に “1” をおくことはできない。

$M$ の値が小さいのが取っかかり。

たとえば、$S=\{1,2,5,7\}$ とすると、$DP[i-1, S]$ の状態からは、$A_{i}$ には4種類の数字を置ける。

         i-1  i
... 1  3  6   ?     ← 1 or 2 or 5 or 7 を置ける

$Y_i$ を、「$X_j=i$ であるような $j$ の集合」とし、前計算しておく。 (意味合い的には、$i$ を置いたら次以降に置いて大丈夫になる数字)

$A_i$ に $k$ を置くとすると、$DP[i, S \backslash \{k\} \cup Y_k] += DP[i-1,S]$ というように遷移できる。

$2^M$ 通りにつき、“1”が立ってるbitからそれぞれ遷移できて、それを $N$ 回繰り返すので、計算量は $O(NM2^M)$

Python3

D - Portable Gate

D - Portable Gate

問題文

制約

解法

要は、ゲームなんかであるファストトラベル的なイメージ。
既に訪れたことのある頂点を同時に1つまで登録でき、いつでもそこにノーコストで戻れる。
登録の上書きもできるが、そうすると以前登録されていた頂点は失われてしまう。

開始頂点が決まっている場合

開始頂点を根とした木DPで解ける。

探索は根から葉へ進む。基本的に、木の頂点を全て巡る移動といえばオイラーツアーのようになる。

ゲートは既に訪れた頂点を登録できるので、 オイラーツアーのコストの内、「ある頂点にゲートを設置しておき、その子の部分木を全て探索し終わったら、戻るのに必要なコスト」を節約するという使い方となる。

    :
    ●←ゲート
    /\
  ●  ○        ゲートの位置まで戻るコスト3を、
  /\            ゲートを設置することで節約できる
 ●●
   |
   ●←駒
  (u)
  / \
(v) (w)

$u$ にゲートを置き、$v$ 以下の探索が終わったら戻るのに使おうと思うと、 $v$ 以下の探索の過程ではゲートを1回も使うことはできない。

ただ、$v$ では使わなくても、兄弟である $w$ 以下では使うことができる。 $w$ から $v$ に移動するとき(または探索順は逆でもいいが)、$u$ を経由するので、その時に再設置できる。 兄弟間は独立に、「親にゲートを設置して戻るのに使う」のか、「自分以下の部分木内で使う」のかを選べる。
(または、$u$ より上位の頂点 $x$ にゲートを設置する場合は $v,w$ のいずれでも使うことはできないが、その場合のコストは、$x$ でDPを計算する際にまとめて算出できる)

以下の木DPをする。

問題の設定では、全てを黒で塗りおえたら、開始頂点に戻らずそこで終えてよい。
木DPでは、基本的には親に帰らないといけないので $v$ まで戻るコストを考慮するが、 最後だけは、「全て塗りおえてから、根に戻るコスト」を省略できる。
$s_v$ は、それを考慮するものとなる。

頂点 $\mathrm{DP}[u]$ を求めたい場合、まず子ごとに寄与を計算する。
子 $v$ が $\mathrm{DP}[u]$ に寄与する分をそれぞれ、$p'_{uv},q'_{uv},r'_{uv},s'_{uv}$ とする。

これらを兄弟間で統合したものが、DP[u]となる。

最終的に、根を $k$ として、$p_k-s_k$ が、開始頂点を $k$ とした場合の答えとなる。

全方位木DP

ここまででも結構な考察だが、実際は開始する頂点を自由に選べる。

根についての木DPの結果を全ての頂点について求めたいと言えば、全方位木DP。

ある程度、ライブラリで実装を抽象化していれば、 根を固定した場合の処理を定義するだけで、自動的に全方位木DPも可能となる。

全て一から実装するのは少し大変かも。

Python3