[yukicoder] No.1888 Odd Insertion

スポンサーリンク

概要

問題文 →
自分の提出 →

解説の貪欲法とは違ったので一応.考察はこちらの方が簡単だという気がする.

解法

$S = (A_1, A_2, \ldots, A_N)$, $P=(1,2,\ldots,N)$ となるように変数変換しておきます($S$ に対する操作は,最小要素などではなく左端などと読み替えます).

$i$ 回目の操作終了時点での集合を $S_i$ とすると,$S_i$ は次のものに限定されます:$A_{1+i}$ よりも右の要素は全て残っており,$\{A_1,\ldots,A_{1+i}\}$ のうちちょうど 1 つが $S$ に残っている.

したがって本問題は,次のような状態に関する dp で解くことができます:

  • $i$ 回目の操作終了時点で $x\in S$ とすることが可能であるか否かの bool を dp[i][x] と定める.

この dp を適切に高速化しましょう.

$i$ 番目の操作での遷移について考えます.考えるべきは次の 2 点です.

  • (1):dp[i-1][x] が true であるような $x$ について dp[i][x] が false になるかどうかを考える.
  • (2):dp[i][A[1+i]] が true, false どちらであるか否かを考える.

dp[i-1] から dp[i] への変更箇所の $i$ にわたる合計は $N$ 以下(各要素は true から false に 1 回以下変更されるのみ)なので,(1) の変更箇所の列挙と (2) の判定が高速にできれば,dp テーブルの書き変えは in-place にやっていけばよいです.

(1) について考えます.$x$ を $S$ に残したまま $A[1+i]$ を $P$ に挿入できるかどうか考えることになります.すると,$x$ が $A[1+i]$ よりも小さい・大きいかのどちらであるかによって true, false が決まるということが分かります.よって,現在 true となっているような $x$ 全体を set や priority queue などで持っておき,先頭または末尾から順番に辿っていけばすべての false への変更を列挙できます.

(2) について考えます.つまり $A[1+i]$ を集合 $S$ にキープしたまま dp[i][x] が true であるような $x$ を挿入できるかという問題です.これは $x$ を添字とするセグメント木を作って,あるセグメントの中で $A_1, \ldots, A_i$ として現れるものの個数,そのうち $dp[i][x]$ が true であるようなものの個数などを管理すれば求めることができます.

以上を適切に実装すれば $O(N\log N)$ 時間で Yes/No を判定することができます.

Yes の場合の復元については,(2) の計算のついでに 「$x$ を $S$ に残すためには $y$ が $S$ に残しておけばよい」という関係を記録していけばよいです.

タイトルとURLをコピーしました