AGC005 B Minimum Sum
昨日解いていて、解説を見ても理解するのに手こずったので、考え方を書いてみる(一応この問題について書いてるブログはあったけど、解法が違ったので、自分用に)。
B: Minimum Sum - AtCoder Grand Contest 005 | AtCoder
TLE解法
自力で思いついたのは全区間の最小値を順番に足していく の解法。
調べる区間の幅をだんだん大きくしていくという考え方。
Submission #1101992 - AtCoder Grand Contest 005 | AtCoder
ansの初期値に代入している は、隣り合う1項の最小値(つまりその項自身)の合計値。数列には から までの数がそれぞれ1回ずつ出てきているので、等差数列の和と考えて計算する。
repの中では、隣り合う 項の最小値を計算してansに足していく。
区間(部分数列)でいうと
{} {} … {}
{} {} … {}
…
{} {}
{}
の最小値を順番に求めている。
setを使った解法
の解法は「最小値が となる区間がいくつあるかを計算し、その区間数と の積の合計を求める」。
が最小値となる区間は、数列の より前の部分で に一番近いところにある よりも小さな値との距離、数列の より後ろの部分で に一番近いところにある よりも小さな値との距離がわかれば求めることができる。
たとえば、{5, 4, 9, 2, 1, 8, 6, 7, 3}という数列の6が最小値となる区間を考える。
6が最小値になる区間は6より小さな値が存在しないので、もとの数列の6より小さい値にだけ色をつけると
{5, 4, 9, 2, 1, 8, 6, 7, 3}
となり、6より小さな値(赤色の数字)が存在しないような区間(部分数列)は{8,6}{8,6,7}{6}{6,7}の4つとなる。
ここで、6が最小値となる区間の左端は、はじめの数列の「6より左にあって6より小さな一番6に近い値(=1)」よりも右で、6よりも右ではない数字になっていることがわかるので、6が最小値となる区間の左端は8,6のどちらかであり、この8,6という左端の候補の数(=2)は「6より左にあって6より小さな一番6に近い値(=1)」と6との距離である。
同様に、6が最小値となる区間の右端は6,7のどちらかであり、右端の候補の数(=2)は「6より右にあって一番6に近い6より小さな値(=3)」と6との距離である。
そして、6が最小値となる区間の数は(左端の候補の数)*(右端の候補の数)より2*2=4個と求めることができる。
今見ている数字 が最小値となる区間の数を求めるためには、 の左右それぞれで、より小さく に一番近い数との距離が必要になる。 よって、要素を自動的にsortしてくれるsetに、 より小さな値の添字を入れておき、 の添字iに一番近い数をlower_boundを使って高速に調べればよい。
が最小値となる区間を調べる際に必要な情報は「 よりも小さな値がどこにあるのか」なので、できるだけ小さな 、つまり1から順番に調べていくと、区間数を調べた後にsetにをそのまま入れることができるので、効率よく求めることができる。
setの要素の挿入に 、set内のlower_boundに かかるので、それぞれに対して行っても 。
Submission #1102173 - AtCoder Grand Contest 005 | AtCoder
入力を配列aで受け取り、配列orderには、区間数を調べる の順番になるように、 の添字iを格納する。つまり、a[order[0]]=1, a[order[1]]=2, … , a[order[N-1]]=Nとなるように格納する。
はじめにsetに入れた-1とNは番兵の役割をする。の前に より小さな値が存在しないときは-1が返され、の後ろに より小さな値が存在しないときはNが返される。
その他の解法
pekempeyさんがUnion-Findを使った解法を解説しています。
AtCoder Grand Contest 005: B. Minimum Sum - pekempeyのブログ
また、standingsで他の人のソースコードを見てみると、stackを使った解法などもあるようです(私にはよくわからなかったのですが……)。
最後に
この問題が400点なんて嘘ですよね?(500はありそうだと思った)