2019年5月26日日曜日

ABC 127 F - Absolute Minima

ABC 127 F - Absolute Minima

更新クエリをkk回処理したあとの関数をfkf_kとすると、fkf_k

fk(x)=xa1+xa2+...+xak+Bf_k(x) = |x-a_1| + |x-a_2| + ... + |x-a_{k}| + B

という形に書ける。BBはクエリの定数項bbの総和である。

以下、(ai)(a_i)はソート済み(a1a2...aka_1 \le a_2 \le ... \le a_{k})として考える。xxa1,a2,...,aka_1, a_2, ..., a_{k}で区切られた数直線を右に進んでいくとき、fk(x)f_k(x)の最初の傾きはk-kであり、aia_iを通過するたびに2ずつ増えていくので、中央値のあたりで最小値を取ることがわかる。

(ただし、この辺の理解はコンテスト後に整理したもので、コンテスト中はDesmosでグラフを描いて、真ん中あたりで最小になるというイメージを得てすぐに先に進んだ。競プロ的には常識の性質らしい。)

具体的には、偶数k:=2nk:=2nについてはx[an,an+1]x \in [a_{n}, a_{n+1}]のときに最小値を取り、代表値としてx=anx=a_{n}を選ぶと

xa1+xa2+...+xa2n=(ana1)+(ana2)+...+(anan)+(an+1an)+(an+2an)+...+(a2nan)=(a1+a2+...+an)+(an+1+an+2+...+a2n) \begin{aligned} & |x-a_1| + |x-a_2| + ... + |x-a_{2n}| \\ = & (a_{n}-a_1)+(a_{n}-a_2)+ ... + (a_{n}-a_{n}) + \\ & (a_{n+1}-a_{n})+(a_{n+2}-a_{n})+ ... +(a_{2n}-a_{n}) \\ = & -(a_1+a_2+ ... +a_{n})+(a_{n+1}+a_{n+2}+ ... +a_{2n}) \end{aligned}

となる。奇数k:=2n+1k:=2n+1についてはx=an+1x=a_{n+1}のときに最小値を取り、

xa1+xa2+...+xa2n+1=(an+1a1)+(an+1a2)+...+(an+1an)+(an+2an+1)+(an+3an+1)+...+(a2n+1an+1)=(a1+a2+...+an)+(an+2+an+3+...+a2n+1) \begin{aligned} & |x-a_1| + |x-a_2| + ... + |x-a_{2n+1}| \\ = & (a_{n+1}-a_1)+(a_{n+1}-a_2)+ ... + (a_{n+1}-a_{n}) + \\ & (a_{n+2}-a_{n+1})+(a_{n+3}-a_{n+1})+ ... +(a_{2n+1}-a_{n+1}) \\ = & -(a_1+a_2+ ... +a_{n})+(a_{n+2}+a_{n+3}+ ... +a_{2n+1}) \end{aligned}

これに上の定数BBを足せば求値クエリへの答えになる。

実装について。更新クエリ(a,b)(a, b)を処理するにはソートされた列に(順序を保って)挿入する操作が必要になり、また求値クエリではその列の区間の和を計算しなければならない。平衡二分木ならどちらもO(logQ){\mathcal O}(\log Q)でできる。

コンテスト中にはImplicit Treapを使ったのだが、二分探索を用意するのを忘れていたのでその場で実装することになった。準備不足とも言えるけど、コンテスト中にデータ構造を多少いじること自体は避けられないと感じているので、構造に習熟しつつテスト環境を充実させる方向のほうが筋がよい気がする。

ただ、この用途で平衡二分木はややリッチすぎる気もした。真ん中で分割した2つの列にしか興味がないので、優先度付きキューを2つ使うくらいでもできるらしい。オーダーは変わらないけど……