AtCoder NoviStepsを埋めてみる(20) SortedSet 1Qの続きです。今回もSortedSetです。

SortedSet を使った便利クラス?

SortedSet.GetViewBetweenメソッドで取得した要素が空の場合、Max, Min が 0 になってしまう問題があったので最初に番兵を追加していましたが、以下の方法で回避できます。

要は SortedSet.GetViewBetween(v, long.MaxValue).Take(cnt).ToList() とやることで v 以上の要素を cnt 個取得できるので cnt を 1 にすれば v 以上の最小の要素を取得できるのです(そのような要素が存在しない場合は返されるリストは空)。

また多重集合も扱えるように Multiset クラスも定義してみました。

E – Cover query

E – Cover query

問題の概要

N 個のマスが左右一列に並んでいる。最初、すべてのマスは白く塗られている。
Q 個のクエリが与えられるので、順に処理せよ。
クエリ:L[i], R[i] 間のマスをすべて黒く塗る。そのあと N 個のマスのうち白く塗られているマスの個数を求めよ。

黒く塗られている部分を区間として考えます。

区間は半開区間 a 以上 b 未満 [a, b) で管理します。a を SortedSet で管理し、それと対応する b を Dictionary で管理します。すでに存在する区間と重複する区間が追加されるときはその区間を削除してから追加します。

まず [left, right) を追加するとき、この右側にこれと重複する区間があるか調べます。left 以上で right 以下の要素がすでに SortedSet 内に格納されていたら、この区間はこれから追加しようとしている区間と重複しています。この場合はこれらの区間はすべて取り除きます。そして取り除いた要素と対応する右側の座標が right よりも大きければこの値で right を更新します。

次に左側に [left, right) と重複する区間があるか調べます。SortedSet に格納されている値のなかで left 以下で最大のものを取得します。これに対応する右側の座標が left 以上であれば重複しています。これを取り除いて、その左右の座標と left, right を比較して外側にあるのであれば left, right を更新します。

最後に更新された left, right で 半開区間 [left, right) を追加します。

区間の削除と追加で白く塗られているマスの個数の変化量がわかるので、差分から白マスの個数を計算して出力します。

E – 1D Bucket Tool

E – 1D Bucket Tool

問題の概要

1 から N の番号がついた N 個のマスが一列に並んでいる。最初、マス i は色 i で塗られている。
クエリが Q 個与えられるので、順に処理せよ。
クエリ 1: マス x と隣接している同じ色のマスをすべて色 c に塗り替える。
クエリ 2: 色 c で塗られているマスの個数を出力せよ。

同じ色で塗られているマスの固まりは半開区間で管理するとよさそうです。「最初はマス i は色 i で塗られている」ので [0, 1), [1, 2), [2, 3), … のようになっています。クエリ 1 で色が塗り替えられたら両隣の区間の色と比較して同じ色なら区間をマージします。クエリ 1 で塗り替えられるのは x とマージされている区間だけなので各色の増減を管理すればクエリ 2 に高速で対応できます。

E – Best Performances

E – Best Performances

問題の概要

長さ N の数列 A があり、最初はすべての項が 0 である。
合計 Q 回のクエリを処理せよ。
(クエリ)A[X[i]] を Y[i] に更新する。値が大きいもの K 個の総和を出力せよ。

これは多重集合なので Set ではなく Multiset を使います(Multiset の定義は上記のとおり)。

ふたつの Multiset を定義します。片方は値が大きい要素 K 個 を格納するもの(larges)で、他方はそれ以外を格納するもの(others)です。値が更新されたら Multiset に格納されている古い値は削除して更新された値を格納します。

値の更新によって 値が大きい要素 K 個 の入れ替えが起きる場合があります。これは larges.Min() < others.Max() であるかどうかを調べるだけでよいです。larges.Min() < others.Max() のときはそれぞれの要素を削除して反対側の Multiset に格納しなおします。

larges に対する要素の削除と追加で値が大きいもの K 個の総和を高速に得ることができます。

E – Least Elements

E – Least Elements

問題の概要

長さ N の整数列 A と整数 M, K が与えられる。
i = 1, …, N – M + 1 に対して、次の独立な問題を解け。
(問題)M 個の整数 A[i], A[i + 1], …, A[i + M – 1] を昇順に並べ替えたときの先頭 K 個の値の総和を求めよ。

E – Best Performances と似た問題です。ふたつの Multiset (smalls, others) を定義し、最初の K 個の要素は smalls に追加し、M 個までは

smalls.Max() ≦ A[i] であれば others に、そうでなければ smalls に追加してもっとも大きい要素は追い出して others にいれなおします。それ以降は A[i] を追加して A[i – M] を削除、smalls.Max() > others.Min() であるなら該当するものを入れ替えるという処理を繰り返せばよいです。

E – Wrapping Chocolate

E – Wrapping Chocolate

問題の概要

縦 A[i] 横 B[i] のチョコレートが N 枚、縦 C[i] 横 D[i] の箱が M 個 ある。
ひとつの箱にはひとつのチョコレートしか入れられない。箱より大きなサイズのチョコレートは入れられないという条件ですべてのチョコレートをすべて箱に入れることは可能か判定せよ。

チョコレートと箱を幅で降順ソートします。もし同じ幅なら箱が先にくるようにソートします。

あとは順番に見ていくのですが、それが箱だった場合は多重集合 S に高さを格納します。チョコレートだった場合は S のなかからチョコレートの高さ以上で最小のものを取り出してマッチングします。もし途中でマッチングさせることができなくなったら答えは “No” であり、最後まで処理を続けることができた場合は “Yes” です。

E – Simple String Queries

E – Simple String Queries

問題の概要

長さ N の英小文字から成る文字列 S が与えられます。
Q 個のクエリを処理せよ。
クエリ 1:S の X[i] 文字目を C[i] に変更する。
クエリ 2:S の L[i] 文字目から R[i] 文字目までの部分文字列に表れる文字が何種類あるかを出力せよ。

文字は 26 種類しかないので、’a’ から ‘z’ が出現する index を SortedSet に格納して L[i] 文字目から R[i] 文字目までに各文字が存在するかを調べます。