−目次
Sky株式会社プログラミングコンテスト2023(AtCoder Beginner Contest 329)E,G 問題メモ
E - Stamp
問題
- 長さ N の英大文字からなる文字列 S と、長さ M の英大文字からなる文字列 T
- 長さ N の文字列 X=
'###…#
' に、以下の操作を好きなだけ繰り返して、S にすることができるか判定せよ- X の連続する長さ M の好きな位置を、T で置き換える
- 1≤N≤2×105
- 1≤M≤5
解法
M が小さいので、「S の i 文字目と合わせることが可能な T のindexの集合」を持って、S の左から状態を管理する。
12345 T: ABABC S: A B A B A B C C A B C 1 2 3 4 1 2 3 4 5 1 2 5 1 2 3 4 5
S の i 文字目と合わせられる T のindexの集合を Di とする。(たとえば D3={1,3},D4={2,4} など)
Di−1 がわかっているとき、Di は、以下を合わせたものとなる。
- 1つ前から継続する
- Di−1 に含まれる要素(k とする)について、Si=Tk+1 なら、k+1
- i−1 より左を作った後、i にスタンプの左端を合わせて押す
- Si=T1 なら 1
- i より右を作った後、i−1 にスタンプの右端を合わせて押す
- M が Di−1 に含まれる場合のみ、Si=Tj であるような任意の j
で、途中で Di がなくなったらアウト。
なくならずに最後までいって、M が DN に含まれていれば成功となる。
G - Delivery on Tree
問題
- N 頂点の根付き二分木があり、根である頂点1に空のカゴがある
- M 個のボールがある。それぞれ頂点 Si に現在位置し、Ti に届けたい
- カゴにボールを積み下ろしすることで、全てのボールを届けたい頂点に持っていきたい
- 各頂点では以下の3つができる
- カゴを隣接する頂点に移動させる
- Si でボール i をカゴに積む
- Ti でボール i をカゴから下ろす
- (つまり、Si でも Ti でもない頂点でボール i を積み下ろしはできない)
- カゴに一度に積めるボールは K 個まで
- 各辺を1往復ずつする軌跡(オイラーツアー)のうち、全てのボールを届けたい頂点に持っていくことが可能な軌跡の個数を \mod{998244353} で求めよ
- 2 \le N \le 10^4
- 1 \le M \le 2 \times 10^5
- 1 \le K \le 10^3
- 各頂点、子は最大2個まで
解法
比較的素直な解法だが、素直にやれば解けるという確信に至るまでの丁寧な考察と、実装力が問われる。
可能不可能の判定
まず、可能不可能を判定する。
- LCA(最小共通祖先)を挟んで両側に (S_i,T_i) があったら、S_i を先に訪れる必要がある。
C こんな(S,T)があったら、Cからは左を先に訪れる必要がある /\ S○ | T
これがボール間で矛盾すると答えは“0”。
矛盾せず、子が2個ある各頂点につき「左/右を先に訪れなければならない」「どちらでもよい」が決まったとする。
この「どちらでもよい」でどうするかが唯一の自由度となるが、 その頂点を訪れたときに既にカゴに積まれていたボールの個数によっては、どちらか/両方が不可能な場合もある。
個数ごとに場合分けが必要そう。(制約も NK=10^7 といい感じだし)
各頂点ですべき操作は決まる
ある頂点でカゴにボールを積み下ろしする最適な順番は、無駄なボールを積まないことを意識するとある程度決まる。
仮に、先に訪れる子を左の子とする。
- ①カゴが親から v に着く
- ②親方面に S_i があって、T_i=v のボールがあったら(積んできてるはずなので)下ろす
- ③左の部分木に T_i があって、S_i=v のボールを積む
- ④左の子に行く
- ⑤…(左の子探索中)…
- ⑥左の子から帰ってくる
- ⑦左の部分木に S_i があって、T_i=v のボールを下ろす
- ⑧右の部分木に T_i があって、S_i=v のボールを積む
- ⑨右の子に行く
- ⑩…(右の子探索中)…
- ⑪右の子から帰ってくる
- ⑫右の部分木に S_i があって、T_i=v のボールを下ろす
- ⑬親方面に T_i があって、S_i=v のボールを積む
- ⑭親方面に帰る
この操作と異なる手順でボールを積み下ろししても、悪くなることはあってもよくなることはない。
(※悪くなる=v に来た時点でのカゴのボールの個数が同じ状況で、上記の操作では K 個を超えないが、異なる操作では超えてしまう)
(※よくなる=v に来た時点でのカゴのボールの個数が同じ状況で、上記の操作では K 個を超えてしまうが、異なる操作では超えない)
なので、親・左・右別に、S_i=v,T_i=v となる個数、計6通りを数えておくと、操作を追えそう。
子が1個なら①~⑦⑬⑭、子がない葉頂点なら①②⑬⑭だけでよいが、いずれにしろ最適な操作は決まる。
木DP
- DP[i,j]= 頂点 i に、カゴの中のボール j 個で親から訪れたときに、取れるパターン数
i,j に対し、①~⑭の操作をたどることで遷移を考える。
先ほどの操作の番号を、そのまま積み下ろしするボールの個数として扱うと、
- DP[v,k]=DP[l,k-②+③] \times DP[r,k-②+③+⑤-⑦+⑧] … (A)
となる。ただし、l,r は v の左右の子、⑤は「左に行って帰ってきた際に変動するボールの個数(左の部分木内で積まれた数-下ろされた数)」とする。
⑤や⑩は、S_i に+1、T_i に-1をして、葉から根に向かって累積和を取っていくことで各頂点計算できる。
各操作の途中で 0 個を下回ったり、K 個を超えるような k は無効なので、その場合は DP[v,k]=0 となる。
②,③,⑤,⑦,⑧,…,⑬ でカゴのボールの個数が変化するごとに逐一個数チェックし、途中一度でも超えるとアウト。
上記の遷移(A)は、子が1個の場合は DP[l,k-②+③] のみとなる。
また、どちらを先に訪れても良い頂点なら、右頂点を先に訪れた場合も同様に計算して足し合わせる。
このように木DPをおこない、最終的に DP[1,0] が答えとなる。O(NK)