公式

E - 図書館の貸出管理 / Library Loan Management 解説 by admin

Gemini 3.0 Flash (Thinking)

Overview

This problem asks you to count the number of books on the bookshelf within a specific range \([L, R]\) that have been returned by a given date \(T\). If that count is at most \(K\), output the count; otherwise, output \(-1\).

Analysis

In this problem, each book \(i\) has two parameters: “position \(i\)” and “return date \(D_i\)”. For each query, we need to count elements satisfying two conditions: “position is within \([L, R]\)” and “return date is at most \(T\)”. This can be viewed as a 2D range query problem.

Naive Approach

For each query, checking every book in the range \([L, R]\) one by one to determine whether its return date is at most \(T\) results in a worst-case time complexity of \(O(N \times Q)\). Since \(N, Q \leq 10^5\), the number of operations can reach up to \(10^{10}\), which will not fit within the time limit.

Efficient Approach (Offline Processing)

Notice that we do not need to answer queries in the order they are given (i.e., we don’t need to be online). If we process queries in increasing order of date \(T\), we only need to “add books to the shelf” in increasing order of their return dates \(D_i\).

  1. Sort the books in ascending order of return date \(D_i\).
  2. Sort the queries in ascending order of date \(T_j\).
  3. Process queries from the earliest date onward, adding all books with return dates up to the current query’s date \(T_j\) into a data structure (such as a Fenwick tree).
  4. Use the data structure to retrieve the total number of books in the range \([L_j, R_j]\).

This technique of “advancing time while updating the state” is known as sweep line or offline query processing.

Algorithm

The solution is computed using the following steps:

  1. Preparation:
    • Store each book’s information as a pair (return date, book number) in a list and sort it in ascending order of return date.
    • Store each query’s information as a tuple (date, L, R, original index) in a list and sort it in ascending order of date.
  2. Fenwick Tree (Binary Indexed Tree: BIT) Initialization:
    • Prepare a BIT of length \(N+1\). This is used to set \(1\) at the position of each “currently shelved book” and efficiently compute range sums.
  3. Query Processing:
    • Process the sorted queries in order.
    • For the current query with date \(T_j\), add all not-yet-added books whose return dates are at most \(T_j\) to the BIT (add \(+1\) at position \(i\)).
    • Using the BIT, compute the sum over the range \([L_j, R_j]\) (sum(R_j) - sum(L_j - 1)). This gives the number of books on the shelf, \(c_j\).
    • If \(c_j \leq K\), record \(c_j\) as the answer; otherwise, record \(-1\).
  4. Output:
    • After processing all queries, output the answers in the original index order.

Complexity

  • Time Complexity: \(O((N + Q) \log N + Q \log Q)\)
    • Sorting the books and queries takes \(O(N \log N + Q \log Q)\).
    • Adding to the BIT (\(N\) times) and computing range sums (\(Q\) times) each take \(O(\log N)\), giving \(O((N + Q) \log N)\) in total.
  • Space Complexity: \(O(N + Q)\)
    • Arrays for storing books, queries, the BIT, and results use \(O(N + Q)\) memory.

Implementation Notes

  • Fast I/O: Since \(N\) and \(Q\) can be large, in Python you can avoid I/O bottlenecks by using sys.stdin.read().split() and sys.stdout.write().

  • 1-indexed: Fenwick trees are typically implemented as 1-indexed, so book numbers (\(1\) through \(N\)) can be used directly as indices.

  • Restoring order for offline processing: Since queries are sorted for processing, you need to store the original index before sorting so that answers can be output in the original order at the end.

    Source Code

import sys

def solve():
    # Read all input data and split into strings
    raw_data = sys.stdin.read().split()
    if not raw_data:
        return
    
    # Convert all strings to integers efficiently
    data = list(map(int, raw_data))
    
    # N: Number of books, K: Cart capacity, Q: Number of queries
    N = data[0]
    K = data[1]
    Q = data[2]
    
    # Store books as tuples of (return_date, shelf_index)
    # Book i's return date D_i is at data[3 + (i-1)]
    books = []
    for i in range(N):
        books.append((data[3 + i], i + 1))
    
    # Sort books by their return date for offline processing
    books.sort(key=lambda x: x[0])
    
    # Store queries as tuples of (T, L, R, original_index)
    # Queries start from data[3 + N]
    queries = []
    offset = 3 + N
    for j in range(Q):
        L = data[offset + 3*j]
        R = data[offset + 3*j + 1]
        T = data[offset + 3*j + 2]
        queries.append((T, L, R, j))
    
    # Sort queries by their date T to process chronologically
    queries.sort(key=lambda x: x[0])
    
    # Fenwick Tree (Binary Indexed Tree) to count books on the shelf
    # bit[i] stores counts of books returned in specific index ranges
    bit = [0] * (N + 1)
    results = [0] * Q
    book_ptr = 0
    
    # Process queries offline: as T increases, more books are returned to the shelf
    for T, L, R, q_idx in queries:
        # Add all books returned on or before date T into the BIT
        while book_ptr < N and books[book_ptr][0] <= T:
            idx = books[book_ptr][1]
            while idx <= N:
                bit[idx] += 1
                idx += idx & (-idx)
            book_ptr += 1
        
        # Calculate the number of books in shelf range [L, R] using BIT
        # prefix_sum(R)
        s_r = 0
        idx_r = R
        while idx_r > 0:
            s_r += bit[idx_r]
            idx_r -= idx_r & (-idx_r)
        
        # prefix_sum(L-1)
        s_l = 0
        idx_l = L - 1
        while idx_l > 0:
            s_l += bit[idx_l]
            idx_l -= idx_l & (-idx_l)
        
        # Total books returned in interval [L, R] by date T
        count = s_r - s_l
        
        # Compare count with cart capacity K
        if count <= K:
            results[q_idx] = count
        else:
            results[q_idx] = -1
    
    # Output all results in the original query order
    sys.stdout.write('\n'.join(map(str, results)) + '\n')

if __name__ == '__main__':
    solve()

This editorial was generated by gemini-3-flash-thinking.

投稿日時:
最終更新: