B - 本の貸し出し / Book Lending Editorial by admin
Claude 4.6 Opus (Thinking)概要
各生徒に対して「読める本(難易度が読解力以下)の中で最も面白い本」を貸し出し、面白さの合計を求める問題です。同じ本を複数人に貸せるため、各生徒を独立に考えることができます。
考察
重要な気づき
同じ本を何人にでも貸し出せるので、生徒同士で本の取り合いは起きません。つまり、各生徒について独立に「難易度 \(R_i \leq T_j\) を満たす本の中での面白さの最大値」を求めればよいということになります。
素朴なアプローチの問題点
各生徒 \(j\) に対して、\(M\) 冊すべてを調べて \(R_i \leq T_j\) かつ \(S_i\) が最大のものを探すと、計算量は \(O(N \times M)\) になります。\(N, M\) がそれぞれ最大 \(2 \times 10^5\) なので、最悪 \(4 \times 10^{10}\) 回の操作が必要となり、制限時間に間に合いません。
解決のアイデア
本を難易度の昇順にソートしておき、前からの面白さの累積最大値(prefix max)を計算しておきます。
例えば、本が難易度順に並んでいるとき:
| 本 | 難易度 \(R\) | 面白さ \(S\) | prefix max |
|---|---|---|---|
| A | 2 | 5 | 5 |
| B | 3 | 8 | 8 |
| C | 5 | 3 | 8 |
| D | 7 | 10 | 10 |
生徒の読解力が \(T_j = 4\) の場合、難易度 \(4\) 以下の本は A, B, C です。その中の面白さの最大値は prefix max から \(8\) と即座にわかります。
「難易度 \(\leq T_j\) を満たす最後の本の位置」は、ソート済み配列に対する二分探索で高速に求められます。
アルゴリズム
- \(M\) 冊の本を難易度 \(R_i\) の昇順にソートする。
- ソートした順に面白さの累積最大値(prefix max)を計算する。
prefix_max[k]= ソート後の先頭 \(k+1\) 冊の中での \(S_i\) の最大値
- 各生徒 \(j\) について:
- 二分探索(
bisect_right)で、難易度配列の中から \(T_j\) 以下の最後の位置posを求める。 pos >= 0ならprefix_max[pos]を合計に加算する(読める本がある場合)。pos < 0なら読める本がないので \(0\) を加算する。
- 二分探索(
- 合計を出力する。
計算量
- 時間計算量: \(O(M \log M + N \log M)\)
- 本のソートに \(O(M \log M)\)
- 各生徒について二分探索に \(O(\log M)\)、それが \(N\) 人分で \(O(N \log M)\)
- 空間計算量: \(O(N + M)\)
- 本の情報、prefix max 配列、生徒の読解力配列の格納
実装のポイント
bisect_rightの使い方:bisect_right(difficulties, t)は「\(t\) より大きい最初の位置」を返すので、そこから \(1\) 引くと「\(t\) 以下の最後の位置」になります。結果が \(-1\)(= 読める本がない)の場合の処理を忘れないようにします。入力の高速化: \(N, M\) が大きいため、
sys.stdin.buffer.read()でまとめて読み込むことで入出力を高速化しています。prefix max の構築: ソート後に1回のループで構築でき、各クエリへの応答を \(O(1)\)(二分探索部分を除く)にできます。
ソースコード
import sys
from bisect import bisect_right
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
books = []
for i in range(M):
s = int(input_data[idx]); idx += 1
r = int(input_data[idx]); idx += 1
books.append((r, s))
T = [int(input_data[idx + j]) for j in range(N)]
idx += N
# Sort books by difficulty
books.sort()
# Build prefix max of fun values sorted by difficulty
# For each difficulty threshold, the best fun is the max fun among all books with r <= threshold
difficulties = []
prefix_max = []
current_max = 0
for r, s in books:
current_max = max(current_max, s)
difficulties.append(r)
prefix_max.append(current_max)
total = 0
for t in T:
# Find rightmost book with difficulty <= t
pos = bisect_right(difficulties, t) - 1
if pos >= 0:
total += prefix_max[pos]
print(total)
main()
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: