064 – Uplift(★3)

問題の趣旨

区画 i の標高を E[i] とするとき、不便さを |E[1] – E[2]| + |E[2] – E[3]| + … + |E[N-1] – E[N]| と定義する。i回目の地殻変動では、区画[L[i], R[i]]の標高がV[i]だけ変化するが、各地殻変動後の不便さをそれぞれ求めよ。

地殻変動がおきたとき隣の区画との高低差が変動するのは区画[l, r]の内部と外部の境界線だけです。そこでその差分を計算すればよいのですが、ここはあえて遅延評価型のセグメント木で解いてみることにします。

セグメント木で絶対値をどう扱うか?

セグメント木では要素をふたつずつ組み合わせて計算をしていきます。そのためノードを定義して各ノードがカバーする区間の不便さ(隣り合う区間の差の絶対値の総和)と両端の標高を管理する必要があります。

Node構造体の定義

まずNode構造体を定義します。

LazySegtreeOperator構造体の定義

LazySegtreeクラスの型引数として渡す構造体を定義します。

単位元は長さ0です。作用の単位元 FIdentity は 0 です。地殻変動同士が重なった場合は単純にそれらを加算すればよいので Composition(long nf, long cf) は nf + cf です。また地殻変動がおきたときはノードがカバーする区間の両端の値に変動量を加算すればよいので、Mapping(long f, Node x) は x.Left += f; x.Right += f;です。

Operate(Node x, Node y) ではノードがカバーする区間の両端、隣り合う区間の差の絶対値の総和(Math.Abs(x.Right – y.Left)とすでに計算されている絶対値の総和の合計)、区間の長さで新しいNodeオブジェクトを生成して返しています。

コンストラクタ

コンストラクタは以下のとおりです。LazySegtreeオブジェクトを生成して引数の配列の値をセットしています。

区間に値を加算する

区間に値を加算する処理を示します。

区間の総和を計算する

区間の総和を計算する処理を示します。

要素に値を設定したり取得する処理を示します。

RangeAbsSumDiffQueryクラスを使ってみる

定義したRangeAbsSumDiffQueryクラスを用いて解を出力します。