考え方
公式解説がわかりやすい.二分探索+DP.Editorial - AtCoder Beginner Contest 236
平均値
\begin{aligned}
& \frac{1}{n} \sum_{i=1}^{n} x_{i} \geq K \\
& \Leftrightarrow \sum_{i=1}^{n} (x_{i} -K) \geq 0
\end{aligned}
と読み替える.& \frac{1}{n} \sum_{i=1}^{n} x_{i} \geq K \\
& \Leftrightarrow \sum_{i=1}^{n} (x_{i} -K) \geq 0
\end{aligned}
中央値
\begin{aligned}
y_{i} =
\begin{cases}
\, 1 & (x_{i} \geq K)\\
\, -1 & (x_{i} < K)
\end{cases}
\end{aligned}
とする.y_{i} =
\begin{cases}
\, 1 & (x_{i} \geq K)\\
\, -1 & (x_{i} < K)
\end{cases}
\end{aligned}
「(中央値) $\geq K$」は,$K$以上の値が半分より多い (※)($\lceil n/2 \rceil $個より多い)ことと同値であるから,
\begin{aligned}
& \mathrm{median\,} (\{x_{1}, x_{2},...,x_{n} \}) \geq K \\
& \Leftrightarrow \sum_{i=1}^{n} y_{i} > 0
\end{aligned}
と読み替えられる.& \mathrm{median\,} (\{x_{1}, x_{2},...,x_{n} \}) \geq K \\
& \Leftrightarrow \sum_{i=1}^{n} y_{i} > 0
\end{aligned}
(※) 例えば,$\{1,1,2,2\}$の中央値は$1$.
二分探索+DP
これまでの内容を使って,「平均値 / 中央値 が$K$以上になるか?」を二分探索する.その際,「平均値 / 中央値 の最大値」を求めるのには,次のDPを使う.ただし,平均値の計算では$B_{k} = x_{k} -K$とし,中央値の計算では$B_{k} = y_{k}$とする.- $\mathrm{used\,}[i] = i$番目まで決定する & $i$番目を使う場合の $\sum_{k} B_{k}$の最大値
- $\mathrm{unused\,}[i] = i$番目まで決定する & $i$番目を使わない場合の $\sum_{k} B_{k}$の最大値
遷移は
\begin{aligned}
\mathrm{used\,}[i+1]
&=\max(\mathrm{used\,}[i], \mathrm{unused\,}[i]) + B_{i+1} \\
\mathrm{unused\,}[i+1]
&=\mathrm{used\,}[i]
\end{aligned}
となる.\mathrm{used\,}[i+1]
&=\max(\mathrm{used\,}[i], \mathrm{unused\,}[i]) + B_{i+1} \\
\mathrm{unused\,}[i+1]
&=\mathrm{used\,}[i]
\end{aligned}
回答例
$2^{60} \sim 10^{18}$だから,誤差okのはず.N = int(input()) A = list(map(int, input().split())) # --- average --- ok, ng = 0, 10 ** 9 for _ in range(60): mid = (ok + ng) / 2 B = [a - mid for a in A] used, unused = 0, 0 for b in B: used, unused = max(used, unused) + b, used if max(used, unused) >= 0: ok = mid else: ng = mid print(ok) # --- median --- ok, ng = 0, 10 ** 9 + 1 while ng - ok > 1: mid = (ok + ng) // 2 B = [1 if a >= mid else -1 for a in A] used, unused = 0, 0 for b in B: used, unused = max(used, unused) + b, used if max(used, unused) > 0: ok = mid else: ng = mid print(ok)
【参考】
Submission #28748466 - AtCoder Beginner Contest 236
【解説 実況】ABC236 E問題【かつっぱ】 - YouTube
アライグマ「E問題は答えを二分探索なのだ! 「A[i]からいくつか選んで平均がx以上か?」は「A[i]-xからいくつか選んで合計が0以上か?」に、「A[i]からいくつか選んで中央値がx以上か?」は「(A[i]>=x?1:-1)からいくつか選んで合計が0以上か?」に読み替えるのは典型なのだ!」
— 競技プログラミングをするフレンズ (@kyopro_friends) January 23, 2022