ABC474 C - Remove and Append
削除と追加
考え方
並べなおすときには、その値は必ず最後に並ぶ。
ということは、「並べなおす」という行為を最後に行った人ほど後ろにある。
ならば、逆再生で解いていけば簡単である。
クエリを先に全て受け取り、これを逆から見る。
初めて見た値があれば、配列の最後尾から順に入れる。
その数を見たことがあるかを vector<bool> で管理するとよい。
クエリ分が終わったら、元の順列も見る。
これも、逆順に見て初めて見た値があれば、配列の最後尾から順に入れる。
この処理が終わったら、自然と最終状態の順列ができあがっている。
計算量は $O(N+Q)$ である。
入力例1での動作
入力を受け取る。
n: 4
q: 2
p: {2, 4, 3, 1}
a: {3, 2}
まずクエリを逆から見て、初めて見た値を最終状態の後ろから並べる。
| 見る値 | すでに並んでいるか | 並べた後の状態 |
|---|---|---|
| $2$ | いいえ | _ _ _ 2 |
| $3$ | いいえ | _ _ 3 2 |
これで、クエリによって並べなおされた値の位置が決まった。
次に、元の順列を逆から見る。
元の順列を逆から見ると、$1,3,4,2$ の順になる。
すでに並んでいる $2,3$ は飛ばし、初めて見る値だけを残りの位置へ後ろから入れる。
| 見る値 | すでに並んでいるか | 並べた後の状態 |
|---|---|---|
| $1$ | いいえ | _ 1 3 2 |
| $3$ | はい | _ 1 3 2 |
| $4$ | いいえ | 4 1 3 2 |
| $2$ | はい | 4 1 3 2 |
全ての位置が埋まり、最終状態は $4,1,3,2$ となる。
注意点
特になし。
別解
逆再生が思いつかなければ、各値が何番目にいるかを管理してシミュレーションしてもよい。
ただし、どれかが抜けたときに前に詰めるのは計算量が $O(NQ)$ になってしまう。
そこで、抜けたときはそこを空席にして、$N+1$ 番目以降に並ぶと考えることにする。
最終的に各値がいる位置を見て「位置、値」という pair を何らかの方法でソートすればよい。
こうすれば計算量は $O(N\log N+Q)$ で済む。