ARMERIA

Rubyと競技プログラミングの話 AtCoderやCodeforcesの問題解説記事が多め。

AtCoder Grand Contest 038 B - Sorting a Segment

B - Sorting a Segment

公式解説と少し違う方法で解いたので記録。

解法

ある長さ  K区間を昇順に並び替えた時に、値が変更される最左のインデックスを  l 、最右のインデックスを  r とします。この時操作の結果は、閉区間  \lbrack l, r \rbrack 内の要素を昇順に並び替えたものとなります。つまりこの区間  \lbrack l, r \rbrack は「操作によって実質的に並び替えられたのはどの区間」というものを表します。

f:id:betrue12:20190922003715p:plain

求めたい答えは、 N-K+1 通りの全ての操作パターンについてこのようなインデックスの組  \lbrace l, r \rbrace を求めた時の、それらの相異なる要素数となります。ただし1つも要素が変更されない場合もあるので、その場合は適当に  \lbrace l, r \rbrace = \lbrace -1, -1 \rbrace などとしておきます。(開区間と紛らわしいので値の組は  \lbrace \cdot, \cdot \rbrace と表記します)

では各操作パターンについて、長さ  K区間  \lbrack i, i+K-1\rbrack を操作したときの  l, r はどのように求めれば良いでしょうか。左右同様の方法で求められるので  l について考えます。

左端から見て、要素が変更されていない区間  \lbrack i, l-1\rbrack に注目します。この区間に属する全ての値  j\in \lbrack i, l-1\rbrack が満たすべき条件は、 P_{j} より小さい要素が区間  \lbrack j, i+K-1\rbrack に存在しないことです。

f:id:betrue12:20190922003725p:plain

このような条件を満たす区間のうち最も長いものを求めることができれば、その区間 \lbrack i, l-1\rbrack として  l の値を求めることができます。そしてこれは二分探索で求めることができます。

具体的には、あらかじめ各インデックス  j について「 P_{j} より小さい要素が右側で次に現れるのはどこか」を求めておきます(存在しない場合は適当に大きな数とします)。この値を  x_{j} としましょう。

そうすると区間  \lbrack i, l-1\rbrack の値が変更されない必要十分条件は、区間内の全ての  j について  x_{j}区間  \lbrack j, i+K-1\rbrack の外にあること、つまり

 \displaystyle\min_{j\in \lbrack i, l-1\rbrack} x_{j} \gt i+K-1

が成立することなので、 x_{j} の値をSparse Tableやセグメント木などに入れておくことで効率的にこの判定が出来て、二分探索をすることができます。

また  x_{j} の値も、 P_{i} そのものをSparse Tableに入れて二分探索したり、 P_{i} を右から見ていきながらセグメント木に入れていくことで求めることができます。

 r についても、左/右や小さい/大きいを逆にして考えると同様に計算できます。これで各操作に対する  l, r の値が求められるので、 値の組  \lbrace l, r \rbrace をset等に入れて相異なる要素数を数えることで答えを求めることが出来ます。

ACコード

Submission #7637418 - AtCoder Grand Contest 038