整体二分
郑哥狂喜
引入:静态区间第 k 小。给定一个数组和若干个询问,每次询问要查询某个区间中第 \(k\) 小的数。
这里介绍一种整体二分的算法。
如果只有一个询问,有一种二分的算法:
初始左右端点设为整个数组的最小值和最大值,不断二分。看一下二分值 \(mid\),比 \(mid\) 小的数有 \(t\) 个。如果 \(t\ge k\),则答案一定在 \([l,mid]\) 中,否则在 \([r,mid]\) 中。
而查询区间内比 \(mid\) 小的数的个数,可以用 BIT,把所有小于等于 \(mid\) 的都设成 \(1\),大于的设成 \(0\)。
这么做的时间复杂度是 \(O(n\log n\log C)\),\(C\) 是数的值域。
如果每个询问都这么做,复杂度 \(O(nq\log n\log C)\) 太慢。考虑对所有询问整体二分,一次性多处理。
定义一个集合 \(Q\) 初始包含所有询问,一个集合 \(A\) 初始包含数组所有数。
一个函数 \(solve(l,r,Q,A)\) 表示当前二分到区间 \([l,r]\),\(Q\) 中的询问的答案在 \([l,r]\) 中,\(A\) 是原数组中所有值在 \([l,r]\) 中的数的集合。
如果 \(l=r-1\),则 \(Q\) 中所有询问答案为 \(l\)。
当执行 \(solve(l,r,Q,A)\),取 \(mid=(l+r)/2\),枚举 \(A\) 中所有数,如果这个数小于等于 \(mid\),把它的位置在 BIT 上设为 \(1\);否则设为 \(0\)。
然后枚举 \(Q\) 中的询问,利用 BIT 查询它的区间内小于等于 \(mid\) 的数的个数 \(t\),如果 \(t\ge k\),就把他放进一个集合 \(q1\);否则放入 \(q2\)。
同时 \(A\) 中小于等于 \(mid\) 的放入集合 \(a1\),大于的放入 \(a2\)。
然后递归 \(solve(l,mid,q1,a1),solve(mid,r,q2,a2)\)。