E - 図書館の貸出管理 / Library Loan Management Editorial by admin
GPT 5.2 High概要
各問い合わせ \((L,R,T)\) について、「区間 \([L,R]\) にある本のうち返却予定日 \(D_i \le T\) を満たす本の数 \(c\)」を数え、\(c \le K\) なら \(c\)、超えるなら \(-1\) を出力します。
考察
本 \(i\) が日付 \(T\) に書架にある条件は \(D_i \le T\) です。したがって各問い合わせは - 配列 \(D\) に対して - 条件 \(D_i \le T\) を満たす要素が - インデックス区間 \([L,R]\) にいくつあるか
という 「しきい値つき区間個数クエリ」 になっています。
素朴に各問い合わせごとに \(i=L..R\) を走査すると、最悪で \(O(NQ)\)(\(10^{10}\))となり間に合いません。また、\(T\) が問い合わせごとに異なるため、単純な前計算(例えば日付ごとの累積)も日付が最大 \(10^9\) で不可能です。
重要な気づきは次の2点です: 1. 条件 \(D_i \le T\) は、\(T\) が増えるほど「書架にある本」が単調に増える。 2. 問い合わせを \(T\) の昇順に処理すれば、「新たに書架に戻った本」だけを追加していけばよい。
つまり、オフライン処理(クエリをソートしてまとめて処理) によって高速化できます。
アルゴリズム
方針(オフライン + BIT)
- 本を \((D_i, i)\) の形で持ち、\(D_i\) の昇順にソートする。
- 問い合わせを \((T_j, L_j, R_j, j)\) の形で持ち、\(T_j\) の昇順にソートする(\(j\) は元の順番に戻すため)。
- Fenwick Tree(BIT)を用意し、BIT の位置 \(i\) には「本 \(i\) がすでに書架にあるなら 1、なければ 0」を入れる。
- ソート済みクエリを小さい \(T\) から順に見ていき、現在の \(T\) 以下の \(D_i\) を持つ本をすべて BIT に追加する(\(add(i,1)\))。
- これにより、BIT は「日付 \(T\) 時点で書架にある本の位置」を表すようになる。
- 問い合わせ \((L,R,T)\) の答え \(c\) は、BIT の区間和 \(sum(R)-sum(L-1)\) で得られる。
- \(c \le K\) なら \(c\)、そうでなければ \(-1\) を記録し、最後に元の順で出力する。
具体例イメージ
例えば \(D=[3,1,4,1,5]\) のとき、\(T=1\) なら \(D_i \le 1\) の位置(2,4)だけが 1 になります。
次に \(T=3\) なら位置 1 も追加され、(1,2,4) が 1 になります。
この「\(T\) を増やすと 1 が増える」性質を、ソート + 追加処理で効率よく実現しています。
計算量
- 時間計算量:
ソートが \(O(N\log N + Q\log Q)\)、各本の追加が合計 \(N\) 回で \(O(N\log N)\)、各問い合わせの区間和が \(Q\) 回で \(O(Q\log N)\)。
よって全体で \(O((N+Q)\log N)\)(同程度の \(\log\) とみなしてよい)。 - 空間計算量: \(O(N+Q)\)(本・問い合わせ配列、BIT、答え配列)
実装のポイント
BIT は 1-indexed なので、本の位置 \(i\) はそのまま \(1..N\) で管理します(コードでは \((D[i], i+1)\))。
問い合わせはソートで順番が変わるため、元の問い合わせ番号 \(j\) を一緒に持ち、
ans[j]に入れて最後に順に出力します。「本の追加」はポインタ
pを使い、books[p].date <= Tの間だけ進めることで、各本をちょうど1回だけ BIT に追加します。これが高速化の肝です。ソースコード
import sys
class BIT:
__slots__ = ("n", "bit")
def __init__(self, n):
self.n = n
self.bit = [0] * (n + 1)
def add(self, i, v):
n = self.n
bit = self.bit
while i <= n:
bit[i] += v
i += i & -i
def sum(self, i):
s = 0
bit = self.bit
while i > 0:
s += bit[i]
i -= i & -i
return s
def range_sum(self, l, r):
return self.sum(r) - self.sum(l - 1)
def main():
input = sys.stdin.readline
N, K, Q = map(int, input().split())
D = list(map(int, input().split()))
books = sorted([(D[i], i + 1) for i in range(N)]) # (date, index)
queries = []
for j in range(Q):
L, R, T = map(int, input().split())
queries.append((T, L, R, j))
queries.sort()
bit = BIT(N)
ans = [0] * Q
p = 0
for T, L, R, qi in queries:
while p < N and books[p][0] <= T:
_, idx = books[p]
bit.add(idx, 1)
p += 1
c = bit.range_sum(L, R)
ans[qi] = c if c <= K else -1
sys.stdout.write("\n".join(map(str, ans)))
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
posted:
last update: