题解:AtCoder AT_awc0098_b Library Book Lending
【题目来源】
AtCoder:B - Library Book Lending
【题目描述】
Takahashi works part-time at a university library. At this library, the books a student can borrow are restricted based on their study level.
There are \(N\) students registered at the library, numbered from \(1\) to \(N\). The study level of student \(i\) is an integer \(S_i\).
The library has \(M\) books, numbered from \(1\) to \(M\). The minimum study level required to borrow book \(j\) is an integer \(T_j\).
Student \(i\) can borrow book \(j\) if and only if \(S_i \geq T_j\).
Note that the same book can be borrowed by multiple students independently. That is, the number of books a student can borrow is not affected by the borrowing status of other students.
For each of the \(N\) students, determine the number of books that student can borrow. Specifically, for each \(i\) \((1 \leq i \leq N)\), find the count \(C_i\) of books \(j\) \((1 \leq j \leq M)\) satisfying \(S_i \geq T_j\).
高橋在一所大学图书馆做兼职。在这家图书馆,学生可以借阅的书籍受到其学习水平的限制。
图书馆有 \(N\) 名注册学生,编号从 \(1\) 到 \(N\)。学生 \(i\) 的学习水平为整数 \(S_i\)。
图书馆有 \(M\) 本书,编号从 \(1\) 到 \(M\)。借阅第 \(j\) 本书所需的最低学习水平为整数 \(T_j\)。
当且仅当 \(S_i \geq T_j\) 时,学生 \(i\) 可以借阅第 \(j\) 本书。
注意,同一本书可以被多名学生独立借阅。也就是说,一名学生可以借阅的书籍数量不受其他学生借阅情况的影响。
对于每名 \(N\) 名学生,确定该学生可以借阅的书籍数量。具体地,对于每个 \(i\) \((1 \leq i \leq N)\),求满足 \(S_i \geq T_j\) 的书籍 \(j\) \((1 \leq j \leq M)\) 的数量 \(C_i\)。
【输入】
\(N\) \(M\)
\(S_1\) \(S_2\) \(\ldots\) \(S_N\)
\(T_1\) \(T_2\) \(\ldots\) \(T_M\)
- The first line contains the number of students \(N\) and the number of books \(M\), separated by a space.
- The second line contains the study levels of each student \(S_1, S_2, \ldots, S_N\), separated by spaces.
- The third line contains the minimum study levels required to borrow each book \(T_1, T_2, \ldots, T_M\), separated by spaces.
【输出】
\(C_1\)
\(C_2\)
\(\vdots\)
\(C_N\)
Output \(N\) lines. The \(i\)-th line \((1 \leq i \leq N)\) should contain \(C_i\), the number of books that student \(i\) can borrow.
【输入样例】
3 4
2 1 3
1 2 3 2
【输出样例】
3
1
4
【核心思想】
-
问题分析:给定 \(N\) 个学生的学习水平 \(S_i\) 和 \(M\) 本书的最低学习水平要求 \(T_j\),需要统计对每个学生的满足 \(S_i \geq T_j\) 的书籍数量 \(C_i\)。若对每个学生暴力遍历所有书籍,时间复杂度为 \(O(N \cdot M)\)。关键观察是:条件 \(S_i \geq T_j\) 等价于 \(T_j \leq S_i\),若将 \(T\) 排序,则问题转化为统计排序后数组中 \(\leq S_i\) 的元素个数,可用二分查找优化。
-
算法选择:
- 排序 + 二分查找:将书籍要求数组 \(T\) 升序排序,对每个学生的 \(S_i\) 用
upper_bound找到第一个 \(> S_i\) 的位置,该位置之前的元素个数即为答案
- 排序 + 二分查找:将书籍要求数组 \(T\) 升序排序,对每个学生的 \(S_i\) 用
-
关键步骤:
- 读取输入:\(N\)(学生数)、\(M\)(书籍数)、数组 \(S[1..N]\)、数组 \(T[1..M]\)
- 排序:
sort(t + 1, t + m + 1),将 \(T\) 按升序排列 - 统计每个学生(\(i\) 从 \(1\) 到 \(N\)):
pos = upper_bound(t + 1, t + m + 1, s[i]) - t - 1upper_bound返回第一个 \(> S_i\) 的迭代器,减去数组首地址得位置下标(\(1\)-indexed),再减 \(1\) 得到 \(\leq S_i\) 的元素个数- 输出 \(pos\)
-
时间/空间复杂度:
- 时间复杂度:\(O(M \log M + N \log M)\),排序 \(O(M \log M)\),每次二分 \(O(\log M)\)
- 空间复杂度:\(O(N + M)\),存储两个数组
-
排序 + 二分查找的核心思想:
- 查询离线化:将"多对多"的匹配问题转化为"一维数组上的前缀计数"问题。排序后,\(\leq x\) 的元素构成一个前缀区间,其长度可通过二分快速确定
- 单调性利用:排序后的 \(T\) 具有单调性,\(T_j \leq S_i\) 的元素必然连续分布在数组前端,使得二分查找适用
- 标准库工具:
upper_bound是 C++ 标准库提供的二分查找函数,返回第一个大于目标值的迭代器,避免了手写二分的边界错误 - 问题等价转化:\(S_i \geq T_j \Leftrightarrow T_j \leq S_i\),将"学生能否借书"的视角转换为"书能否被学生借"的视角,使排序和二分策略自然浮现
- 适用于一对多匹配统计、范围计数、离线查询优化等场景
【算法标签】
整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 200005; // 最大学生/书籍数量
int n, m; // n: 学生数量, m: 书籍数量
int s[N], t[N]; // s[i]: 学生i的学习水平, t[j]: 书籍j的最低学习水平要求
int main()
{
cin >> n >> m; // 读入学生数量和书籍数量
// 读入每个学生的学习水平
for (int i = 1; i <= n; i++)
cin >> s[i];
// 读入每本书的最低学习水平要求
for (int i = 1; i <= m; i++)
cin >> t[i];
// 对书籍的最低学习水平要求数组排序(升序)
// 排序后可以用二分查找快速统计满足条件的书籍数量
sort(t + 1, t + m + 1);
// 对每个学生,二分查找可以借阅的书籍数量
for (int i = 1; i <= n; i++)
{
// upper_bound(t+1, t+m+1, s[i]) 返回第一个大于 s[i] 的位置
// 减去 t 得到该位置的下标(从1开始计数)
// 再减1,得到最后一个小于等于 s[i] 的位置
// 即:满足 t[j] <= s[i] 的书籍数量
int pos = upper_bound(t + 1, t + m + 1, s[i]) - t - 1;
cout << pos << endl; // 输出学生i可以借阅的书籍数量
}
return 0;
}
【运行结果】
3 4
2 1 3
1 2 3 2
3
1
4
浙公网安备 33010602011771号