排序与二分

排序算法、二分答案、lower_bound

排序

竞赛中几乎总是用库函数:

  • C++:sort(a, a+n) 平均 $O(n \log n)$
  • 需要稳定排序:stable_sort
  • 自定义比较:sort(v.begin(), v.end(), [](const auto& x, const auto& y) { return x.second < y.second; });

离散化

值域很大但个数少时,把值映射到 $1\sim k$:

vector<int> all = a;
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
int id = lower_bound(all.begin(), all.end(), x) - all.begin();

二分答案

当「判定是否可行」比「直接求最优」容易时:

  1. 确定答案区间 $[L, R]$
  2. while (L < R)mid,若可行则收缩一侧
  3. 注意边界:mid = (L + R + 1) / 2 避免死循环

二分查找

有序数组上:

  • lower_bound:第一个 $\ge x$
  • upper_bound:第一个 $> x$
  • 个数:upper_bound - lower_bound

三分

单峰函数求极值:在 $[L,R]$ 上取三等分点比较,适用于凸函数或明确单峰的性质题。

计数排序 / 基数排序

值域小(如 $10^6$ 以内)时可 $O(n)$ 或接近线性,配合离散化使用。

例题

例 1:砍树 / 木材加工(二分答案)
有 $n$ 棵树,高度 $h_i$,要得到至少 $m$ 米木材。设统一砍到高度 $H$,可得 $\sum \max(0, h_i - H)$。$H$ 越大木材越少,二分 $H$,每次 $O(n)$ 检查是否 $\ge m$。复杂度 $O(n \log \max h)$。

例 2:逆序对 / 排名(离散化 + 树状数组)
值域 $10^9$ 但只有 $n$ 个数,先排序去重得到排名 id,再在树状数组上从左到右插入,查询比当前值小的个数即为逆序对贡献。体现「排序 + 离散化」组合拳。