AtCoder NoviStepsを埋めてみる(19) SortedSet 2Q以下の続きです。今回もSortedSetです。

再掲

E – Sparse Range

E – Sparse Range

問題の概要

長さ N の整数列 A と正整数 D が与えられる。
以下の条件をともに満たす整数の組 (L, R) の個数を求めよ。
条件: L ≦ i < j ≦ R を満たすすべての整数の組 (i, j) について | A[i] – A[j] | ≧ D

L を固定して R をどれだけ増やせるかを考えます。L ≦ i < j ≦ R を満たすすべての整数の組 (i, j) について | A[i] – A[j] | ≧ D が成立している状態で R を増やすことができる条件は { A[L], A[L + 1], A[L + 2], …, A[R] } のなかで A[R + 1] よりも小さいものすべてが A[R + 1] – R 以下であること、A[R + 1] よりも大きいものすべてが A[R + 1] + R 以上であることです。LowerMax と UpperMin で表すと (LowerMax(sortedSet, x) ≦ A[R + 1] – D || UpperMin(sortedSet, x) ≧ D + A[R + 1]) を満たすことです。

尺取法ですべての L に対して R が取りうる最大値を求めると条件満たすすべての整数の組 (i, j) の個数を得ることができます。

D – Neighbor Distance

D – Neighbor Distance

数直線があり、最初は座標 0 に人 0 がひとりで立っている。
これから、人 1,2,…,N がこの順に到着し、数直線上に立つ。
このとき d[i] を「人 i に最も近い別の人までの距離」と定義する。
人が到着するたびに d の総和を求めよ。

人 i が到着することで d の総和に d[i] が加算されます。また人 i が到着することで 人 i に最も近い別の人から見た「最も近い人」が人 i に変わる場合があります。ひとり到着するごとに最大で 2 人ぶんの変更がおこなわれます。

よってその都度 d の総和を計算するのではなく、差分のみ計算すればよいことになります。

D – Santa Claus 2

D – Santa Claus 2

問題の概要

2次元平面上の 座標(X[i] ,Y[i]) に家があり、座標(Sx ,Sy) にサンタクロースがいる。
サンタクロースは
D[i] = ‘U’ なら (x, y) から (x, y + C[i]) に直線で移動する。
D[i] = ‘D’ なら (x, y) から (x, y – C[i]) に直線で移動する。
D[i] = ‘L’ なら (x, y) から (x – C[i], y) に直線で移動する。
D[i] = ‘R’ なら (x, y) から (x + C[i], y) に直線で移動する。
行動を終えたあとにサンタクロースがいる点と、行動により通過または到達した家の数を求めよ。ただし、同じ家を複数回通過または到達してもそれらは重複して数えない。

X[i] ,Y[i] が大きいので2次元配列を定義することができません。Dictionary で対応します。X 座標と Y 座標ごとに同じ座標の家を SortedSet で管理します。そしてサンタクロースが通った家は削除します。

先に定義しておいた RemoveRange メソッドで削除される家の座標は取得できるので X, Y 両方の SortedSet から忘れずに削除します。

D – Cross Explosion

D – Cross Explosion

縦 H マス、横 W マスのグリッドがある。
はじめ、すべてのマスには壁が 1 個ずつ立てられている。
Q 個のクエリを順に処理した後に、残っている壁の個数を出力せよ。
クエリ: (R[i] ,C[i]) に爆弾を置いて壁を爆破する。
(R[i] ,C[i]) に壁が存在する場合は、その壁を破壊して処理を終了する。
(R[i] ,C[i]) に壁が存在しない場合は、そこから上下左右に見て最初に現れる壁を破壊する。

H × W が 4 * 10^5 なので2次元配列を定義してかんがえます。X 座標と Y 座標ごとに同じ座標にある壁を SortedSet で管理します。(R[i] ,C[i]) に壁が存在するときに破壊される壁はひとつだけ、そうでないときは最大 4 個です。上下左右に見て最初に現れる壁の座標は UpperMin、LowerMax メソッドで取得できます。

E – Mex and Update

E – Mex and Update

問題の概要

長さ N の数列 A が与えられる。
以下の Q 個のクエリを処理せよ。
クエリ:
A[i[k]] の値を x[k] に変更する。
その後、A に含まれない最小の非負整数を出力する。

A に含まれない非負整数を SortedSet に格納しておけば、各クエリごとにUpperMin メソッドを呼び出すことで A に含まれない最小の非負整数を取得できます。

D – LRUD Instructions

D – LRUD Instructions

問題の概要

H 行 W 列のグリッドがあり、上から r[i] 行目、左から c[i] 列目にあるマスは壁である。
駒が(rs ,cs) に置かれている。Q 個のクエリが与えられるので処理せよ。
クエリ:
D[i] = ‘U’ なら (r, c) から (r – L[i], c) に直線で移動する。
D[i] = ‘D’ なら (r, c) から (r + L[i], c) に直線で移動する。
D[i] = ‘L’ なら (r, c) から (r, c – L[i]) に直線で移動する。
D[i] = ‘R’ なら (r, c) から (r, c + L[i]) に直線で移動する。
いずれも壁がある場合やグリッドの外には移動できず、その直前で停止する。
移動後の座標を出力せよ。

H, W の値が大きいので2次元配列を定義することはできません。Dictionary で壁がある行と列を管理し、SortedSet で壁の座標を管理します。

壁がある行と列の移動はLowerMax, UpperMin メソッドが返す値をみることで現在位置と移動先のあいだに壁があるかどうかわかります。壁があるときは直前まで移動して停止します。

壁がない行と列での移動はグリッドの外へでないように移動させるだけでよいです。

D – Draw Your Cards

D – Draw Your Cards

問題の概要

1 から N が書かれた N 枚のカードが裏向きで積まれた山札があり、上から i 枚目のカードには整数 P[i] が書かれている。
この山札を使って、以下の操作を N ターン繰り返す。
操作:
山札の一番上のカードを引いて、そこに書かれた整数を X とする。
場に見えている表向きのカードであって書かれた整数が X 以上であるもののうち、書かれた整数が最小のものの上に、引いたカードを表向きで重ねる。
もし場にそのようなカードがなければ、引いたカードをどれにも重ねずに表向きで場に置く。
その後、表向きのカードが K 枚重ねられた山が場にあればその山のカードをすべて取り除く。
各カードについて、何ターン目に取り除かれるか、あるいは最後まで取り除かれないかを求めよ。

場に置かれているカードを SortedSet で管理します。新たに出されたカードを重ねることができるカードは UpperMin メソッドを呼び出せばわかります。

重なり合ったカードの関係性は ac-library-csharp というライブラリの Dsu クラスを使って管理します。カードを重ねるときに merge してカードが取り除かれたらターン数を代表元に保存します。最後にそれぞれのカードの代表元に記録された値をみれば、そのカードが何ターン目に取り除かれたか or 最後まで取り除かれなかったかがわかります。

参考: ac-library-csharpを使ってみる

D – Sequence Query

D – Sequence Query

問題の概要

空の数列 A がある。
クエリが Q 個与えられるので処理せよ。
クエリ 1: A に x を追加する。
クエリ 2: A の x 以下の要素のうち、大きい方から k 番目の値を出力する。
クエリ 3: A の x 以上の要素のうち、小さい方から k 番目の値を出力する。
ただし k は 5 以下。k 番目の要素が存在しない場合は -1 を出力すること。

数列 A は多重集合なので SortedSet と Dictionary を併用します。

x 以上の要素を k 個取得するには、まず sortedSet.GetViewBetween(x, long.MaxValue).Take(k).ToList() を実行して x 以上の要素を k 種類取得します。そのあと数列 A 内に同じ値が複数存在するかもしれないので、dic を参照して小さい方から k 番目の値を取得します。

x 以下の要素を k 個取得するときもだいたい同じですが、大きい方から探さないといけないので sortedSet.GetViewBetween(0, x).Reverse().Take(k).ToList() と Reverse しなければなりません。

L – スーパーマーケット

L – スーパーマーケット

問題の概要

スーパーマーケットには陳列棚があり、この陳列棚には商品を並べられる列が N 本あります。
列 i には K[i] 個の商品が手前から奥へと一列に並べられており、手前から j 番目の商品の消費期限は T[i, j] である(すべての商品の消費期限は相異なる)。
M 人の客が順番に商品を買う。
i 番目の客はすべての列について現在の時点で手前から A[i] 番目までにある商品のうち最も消費期限の値が大きいものを購入する(A[i] は 1 または 2 である)。
それぞれの客が購入した商品の消費期限の値を求めよ。

一番手前の商品しか買わない客と手前から二番目までの商品も買う客がいるのでちょっとややこしいです。

一番手前の商品の消費期限のみを格納した SortedSet と 手前から二番目の商品のみを格納した SortedSet のふたつを定義します。一番手前の商品が買われたらその列の手前から二番目の商品が一番手前にくるように操作しなければなりません。ふたつの SortedSet のあいだで適切な追加と削除の処理が必要です。

以下のようなクラスを定義します。商品が買われたときに ふたつの SortedSet のあいだで適切な追加と削除の処理をするためのものです。