A - 図書館の蔵書検索 / Library Book Search 解説 by admin
Gemini 3.0 Flash概要
青木君が持っている \(K\) 枚の閲覧許可証のうち、最も高い許可レベルを \(L_{max}\) とすると、難易度が \(L_{max}\) 以下の本はすべて閲覧可能です。この性質を利用して、条件を満たす本の難易度の総和を効率的に求めます。
考察
重要な気づき
青木君は複数の閲覧許可証を持っていますが、ある本 \(i\) が閲覧できる条件は「持っている許可証の中に、許可レベルが \(P_i\) 以上のものが少なくとも 1 枚存在する」ことです。
これを言い換えると、「持っている許可証の中の最大レベルが \(P_i\) 以上である」ということになります。 つまり、青木君が持っている許可証の番号を \(T_1, T_2, \ldots, T_K\) としたとき、 $\(L_{max} = \max(L_{T_1}, L_{T_2}, \ldots, L_{T_K})\)\( をあらかじめ求めておけば、各本 \)i\( について \)Pi \le L{max}$ かどうかを判定するだけで、その本が読めるかどうかが分かります。
なぜこの工夫が必要か
もし、各本について「持っている \(K\) 枚の許可証のいずれかで読めるか」を愚直に 1 枚ずつ確認すると、最悪の場合で \(N \times K\) 回の比較が必要になります。 制約では \(N, K \le 2 \times 10^5\) であるため、計算回数は最大で \(4 \times 10^{10}\) 回に達し、制限時間内に終わりません。 最大値 \(L_{max}\) を先に求めることで、計算量を大幅に削減できます。
アルゴリズム
- 最大許可レベルの特定: 青木君が持っている許可証の番号 \(T_1, \ldots, T_K\) を確認し、それに対応するレベル \(L_{T_k}\) の中から最大値 \(L_{max}\) を求めます。
- 総和の計算:
各本 \(i = 1, \ldots, N\) について順番に難易度 \(P_i\) を確認します。
- もし \(P_i \le L_{max}\) ならば、その本は読めるので合計値に \(P_i\) を加算します。
- そうでなければ、その本は読めないので無視します。
- 結果の出力: 最終的な合計値を出力します。
計算量
- 時間計算量: \(O(N + M + K)\)
- 入力の読み込みに \(O(N + M + K)\)。
- 最大レベル \(L_{max}\) の算出に \(O(K)\)。
- 本の判定と総和の計算に \(O(N)\)。
- 全体として線形時間で動作するため、十分に高速です。
- 空間計算量: \(O(N + M)\)
- 本の難易度と許可証のレベルを保持するためのメモリが必要です。
実装のポイント
高速な入出力: Pythonで入力数が多い(今回は最大約 \(6 \times 10^5\) 個)場合、
input()を繰り返すよりもsys.stdin.read().split()などで一括で読み込む方が実行時間を短縮できます。インデックスの調整: 問題文では許可証の番号が \(1\) から \(M\) の \(1\)-indexed で与えられますが、プログラムの配列(リスト)は通常 \(0\)-indexed であるため、
L[t_id - 1]のように調整が必要です。大きな数値の扱い: 本の難易度の総和は非常に大きな値になる可能性がありますが、Pythonの
int型は自動的に多倍長整数として扱われるため、オーバーフローの心配はありません。ソースコード
import sys
def main():
# 競プロにおける高速な入出力のために、sys.stdin.read().split() を使用して
# すべてのトークンを一度に読み込み、リストに格納します。
# この手法は、多数の入力を個別に読み込むよりも高速です。
data = sys.stdin.read().split()
# 入力が空の場合は終了します。
if not data:
return
# N: 本の冊数, M: 閲覧許可証の種類数, K: 青木君が持っている許可証の枚数
N = int(data[0])
M = int(data[1])
K = int(data[2])
# 閲覧許可証の許可レベル L_j をリストとして取得します。
# 本の難易度 P_i が N 個あるため、L はインデックス 3 + N から始まります。
# 許可証番号 T_k で指定されたレベルを効率的に取得するために、int型のリストに変換します。
L = list(map(int, data[3 + N : 3 + N + M]))
# 青木君が持っている許可証の中で最大の許可レベルを求めます。
# 青木君が持っている許可証番号 T_k は、インデックス 3 + N + M から始まります。
# 本を閲覧できる条件は「難易度 <= いずれかの許可レベル」であるため、
# 保持している許可証の「最大レベル」以下の本はすべて閲覧可能です。
max_level = 0
for i in range(3 + N + M, 3 + N + M + K):
# 許可証番号 T_k は 1-indexed なので、0-indexed のリスト L に合わせるため -1 します。
t_id = int(data[i])
level = L[t_id - 1]
if level > max_level:
max_level = level
# 青木君が閲覧できる本の難易度の総和を計算します。
# 本の難易度 P_i はインデックス 3 から 3 + N - 1 までに格納されています。
ans = 0
for i in range(3, 3 + N):
p_val = int(data[i])
# 本の難易度が青木君の持つ最大許可レベル以下であれば、その本を閲覧できます。
if p_val <= max_level:
ans += p_val
# 計算した総和を出力します。
# Python の int 型は任意精度であるため、大きな合計値でもオーバーフローしません。
print(ans)
if __name__ == '__main__':
main()
この解説は gemini-3-flash-preview によって生成されました。
投稿日時:
最終更新: