https://www.ioi-jp.org/ioi/2007/problem/day1/sails-prob-j.pdf
https://oj.uz/problem/view/IOI07_sails
船に 本の柱が立っていて、 本目の柱の高さは です。
柱は 個の区間に分かれていて、そのうち 個を選んで、帆を張ります。
ある帆の非効率さを その帆と同じ高さで、かつ前にある帆の数とします。
帆の非効率さの総和の最小値を求めてください。
コストが 帆の数 なので、帆の数は平坦にしたほうがいい。
また、柱の順番は関係ないので、ソートしていい感じにできて、
の昇順にすると、帆の数が少ないところに貪欲に入れることになる。
よって、次ができれば良い。
空の multiset があります
以下のクエリを処理した後の multiset を答えてください
- を追加する
- 小さい方から 個選び、 増やす
これは要素の挿入・削除と区間和ができる平衡二分木を自作して、
cnt[i] := (multiset 内の i の個数) とすれば でできる。
自作平衡二分木はやりたくないので、挿入と削除のできる __gnu_cxx::rope を頑張って使う。
区間和は累積和を持てばできる。
__gnu_cxx::rope のリファレンスがないのでかなり苦戦した。
__gnu_cxx::rope< pair<T, U> > を使うと CE になるという罠がある。
637ms / 1000ms
https://oj.uz/submission/212762
__gnu_cxx::rope はGCC拡張だし、平衡二分木の自作はシラバス外なので、もっとうまい方法があることになる。
しかし、自作平衡二分木を使わないと配列をずらすのはむずかしい。
空の multiset があります
以下のクエリを処理した後の multiset を答えてください
- を追加する
- 小さい方から 個選び、 増やす
multiset 内は常に降順に並んでいると考えて、小さいほうから 個を選んで を足すと、値の境界はほとんど変わらないことがわかる。
そこで、 std::multiset<int>
に値の境界を持たせて、
番目に大きい境界から 番目に大きい境界までが である とすると、
個を選んで を足すクエリが、境界を 1 つ追加・1 つずらす操作になって、 で解ける。
63ms / 1000ms
https://oj.uz/submission/213054
これって slope trick ってやつですか なるほど