AtCoder Regular Contest 179 B,D問題メモ
B - Between B and B
問題文
- $1$ 以上 $M$ 以下の整数からなる長さ $M$ の数列 $(X_1,X_2,\dots ,X_M)$ が与えられます.
- $1$ 以上 $M$ 以下の整数からなる長さ $N$ の数列 $A=(A_1,A_2,\dots ,A_N)$ であって, 以下の条件を満たすものの個数を $998244353$ で割ったあまりを求めてください.
- $B=1,2,\dots ,M$ について, $A$ の中で異なる位置にある $2$ つの $B$ の間(両端を含む)には $X_B$ が存在する.
制約
- $1\leq M\leq 10$
- $1 \leq N \leq 10^4$
- $1 \leq X_i \leq M$
解法
たとえば $X_1=5$ のとき、一度 “1” を置いたら、“5” を置くまで、次に “1” をおくことはできない。
$M$ の値が小さいのが取っかかり。
- $DP[i,S]=(A_1,A_2,...,A_i)$ まで確定させて、$A_{i+1}$ には集合 $S$ にある数字を置ける状態の数
たとえば、$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)$
D - Portable Gate
問題文
- 頂点 $1,2,\dots ,N$ の $N$ 頂点からなる木が与えられます. $i$ 番目の辺は頂点 $u_i,v_i$ を双方向に結んでいます.
- すべての頂点ははじめ白に塗られています.
- 駒と不思議なゲートを $1$ 個ずつ用いて次の手順ですべての頂点を黒に塗ります.
- まず好きな頂点を選び, 駒とゲートをその頂点に置きます.
- その後, すべての頂点が黒に塗られるまで次の操作を何度も行います.
- 次のうち $1$ つを選んで実行する.
- 駒が置かれている頂点を黒に塗る.
- 駒が置かれている頂点に隣接した頂点をひとつ選び, その頂点に駒を移動させる, コストが $1$ かかる.
- ゲートが置かれている頂点に駒を移動させる.
- 駒が置かれている頂点にゲートを移動させる.
- コストがかかるのは $2$ 番目の操作のみであることに注意してください.
- かかるコストの合計の最小値を求めてください.
制約
- $2 \leq N \leq 2\times 10^5$
- $1 \leq u_i,v_i \leq N$
解法
要は、ゲームなんかであるファストトラベル的なイメージ。
既に訪れたことのある頂点を同時に1つまで登録でき、いつでもそこにノーコストで戻れる。
登録の上書きもできるが、そうすると以前登録されていた頂点は失われてしまう。
開始頂点が決まっている場合
開始頂点を根とした木DPで解ける。
探索は根から葉へ進む。基本的に、木の頂点を全て巡る移動といえばオイラーツアーのようになる。
ゲートは既に訪れた頂点を登録できるので、 オイラーツアーのコストの内、「ある頂点にゲートを設置しておき、その子の部分木を全て探索し終わったら、戻るのに必要なコスト」を節約するという使い方となる。
: ●←ゲート /\ ● ○ ゲートの位置まで戻るコスト3を、 /\ ゲートを設置することで節約できる ●● | ●←駒
(u) / \ (v) (w)
$u$ にゲートを置き、$v$ 以下の探索が終わったら戻るのに使おうと思うと、 $v$ 以下の探索の過程ではゲートを1回も使うことはできない。
ただ、$v$ では使わなくても、兄弟である $w$ 以下では使うことができる。
$w$ から $v$ に移動するとき(または探索順は逆でもいいが)、$u$ を経由するので、その時に再設置できる。
兄弟間は独立に、「親にゲートを設置して戻るのに使う」のか、「自分以下の部分木内で使う」のかを選べる。
(または、$u$ より上位の頂点 $x$ にゲートを設置する場合は $v,w$ のいずれでも使うことはできないが、その場合のコストは、$x$ でDPを計算する際にまとめて算出できる)
以下の木DPをする。
- $\mathrm{DP}[v] = (p_v,q_v,r_v,s_v)$
- $p_v$:$v$ を出発して部分木を全て巡り $v$ に戻ってくるのに、ゲートを使用した場合の最適コスト
- $q_v$:$v$ の部分木の頂点数
- $r_v$:$v$ を深さ0として、$v$ の部分木で最も深い頂点の深さ
- $s_v$:$v$ を出発して部分木を全て巡り「$v$ に戻ってこなくても良い」場合の最適コストの、$p_v$ との差分
問題の設定では、全てを黒で塗りおえたら、開始頂点に戻らずそこで終えてよい。
木DPでは、基本的には親に帰らないといけないので $v$ まで戻るコストを考慮するが、
最後だけは、「全て塗りおえてから、根に戻るコスト」を省略できる。
$s_v$ は、それを考慮するものとなる。
頂点 $\mathrm{DP}[u]$ を求めたい場合、まず子ごとに寄与を計算する。
子 $v$ が $\mathrm{DP}[u]$ に寄与する分をそれぞれ、$p'_{uv},q'_{uv},r'_{uv},s'_{uv}$ とする。
- $p'_{uv}$ の求め方
- $v$ 以下でゲートを使う場合:$u→v,v→u$ の部分は直接の移動が必要になる
- $p_v+2$
- $u$ にゲートを設置して、$v$ の探索後戻るのに使う場合:
- $v$ 以下はオイラーツアーなので、頂点数 $q_v \times 2$ の直接移動が必要になる
- そのうち、最も深い頂点から帰る(そうなるように探索順を決める)ようにすると、$r_v+1$ だけ省略できる
- $2q_v-r_v-1$
- $p'_{uv}=\min(p_v+2,2q_v-r_v-1)$
- $s'_{uv}$ の求め方
- まずは差分ではなく、最後の頂点を塗りおえるまでの最適コストを計算し、それを $p'_{uv}$ から引く
- $v$ 以下でゲートを使う場合:$u→v$ のみ新たな直接移動が必要になる(帰りは省略できる)
- $p_v-s_v+1$
- $u$ にゲートを設置して、$v$ の探索後戻るのに使う場合:上記と変わらない
- $2q_v-r_v-1$
- $s'_{uv}=p'_{uv}-\min(p_v-s_v+1,2q_v-r_v-1)$
これらを兄弟間で統合したものが、DP[u]となる。
- $p'_{uv}$ は、兄弟間で直接合算
- $q'_{uv}$ は、兄弟間で直接合算
- $r'_{uv}$ は、兄弟間でMAXを取る
- $s'_{uv}$ は、兄弟間でMAXを取る
最終的に、根を $k$ として、$p_k-s_k$ が、開始頂点を $k$ とした場合の答えとなる。