i 番目 (1≤i≤N) の候補者はその候補者より多く票を獲得した候補者が M 人未満であるとき、かつその時に限り当選します」について。当落を分ける分岐点がどこにあるかをしっかり把握しなければ二分探索の判定関数が書けない。人 i の得票が上から M 番目の人の得票に並んでいるとき、人 i は当選する。逆にいえば、上位 M 人の全員が人 i の得票を1票でも上回っていれば人 i は落選する。判定関数はこう。答え X を二分探索するとき、X を除いた票を上位 M 人に配って底上げした結果、M 人全員が
A[i]+X+1
以上になるなら、判定は偽。落選する。では M 人全員を A[i]+X+1
以上に底上げするのに必要な票数がどう計算できるだろうか。自分は最初 M 人の中間獲得票数の合計と追加票の合計で平均を出すような計算をして間違えた。平均は出さなかったけど、M 人の総得票数だけで考えて間違えた。トップの人が過剰に票を持っていたとしても、その余剰分を底上げにまわすことはできない。じゃあ M 人のうち何人が中間時点で A[i]+X+1
以上の票を持っていたかがわかればそれらの人を除外して累積和で必要な追加票数がわかるね。これは解の二分探索の中でさらに行う二分探索であって TLE だった。提出 #58232088 (TLE×19)。これが 22 時 22 分。昨日のコンテストはここまで。■提出 #58278984 (AC / 456 ms)。解の二分探索をやめました。X 票を人 i を含む M+1 人に分配したとして、底が上位 M 人のどの人とどの人のあいだにあるかを二分探索した。さっきの二重の二分探索の中の方を外に出した感じ。18 行目と 19 行目にある、不可判定(-1)と無条件当選判定(0)であるとか、15 行目と 20 行目にあるボーダーラインの -1
だとか、こちらの解法にはさらに罠が多い。TLE になった log 2つの素直な解法とランダム入力に対する答えを突き合わせてデバッグをした。ま、ボーダーラインの方は目指すべきグラフ(ヒストグラム?)の形がイメージできていたので間違えなかったけど。■提出 #58279635 (C++ / AC / 303 ms)。これは log 2つの、解を二分探索する素直な解法の C++ バージョン。C++ では log^2 が許されていたのだ。■制約に殺されたというにはしっかり難しくて満足できる問題だった。いや実際に制約に殺されてるんだけどね、お前に殺されるなら悔いはない。過去2、3回の経験に加えて今回も言えるんだけど、log 1つの差って、ちょっとした視点の違いなんですよ。ちょっと見る角度を変えれば2つ目の log は消える。これが Pairs の教え (20210401p01)。■■■二分探索を尺取りで置き換えることで計算量のオーダーを改善する常套手段にのっとって、ついに二分探索の log を2つとも取り除いた。提出 #58342253 (AC / 970 Byte / 313 ms)。これでますます制約に殺されたとは言いにくくなるんだけど、これがコンテスト時間内にすらすら書けるなら青といわずとっくに黄色になっている。