求中位数为K的区间的数目

给定一个长为 \(n\) 的序列和常数 \(k\),求此序列的中位数为 \(k\) 的区间的数量。一个长为 \(m\) 的序列的中位数定义为将此序列从小到大排序后第 \(\lceil m / 2 \rceil\) 个数。

解法

直接考虑中位数等于 \(k\) 的区间是比较困难的,我们转而考虑中位数大于等于 \(k\) 的区间个数。按题目中所采用中位数定义,一个序列的中位数大于等于 \(k\) 当且仅当序列中大于等于 \(k\) 的元素的数目超过序列长度的一半。

对于某个固定的 \(k\),将序列中大于等于 \(k\) 的元素替换成 \(1\),小于 \(k\) 的元素替换成 \(-1\),则区间的中位数大于等于 \(k\) 就等价于区间和大于 \(0\) 。从而可以用树状数组求出区间和大于 \(0\) 的区间个数。复杂度 \(O(n\log n)\)

若中位数的定义改成排序后第 \(\lceil (m +1)/ 2 \rceil\) 个数,只要将算法稍加修改即可。

优化

给定一个长度为 \(n\) 的由 \(-1\)\(1\) 构成的序列 \(a\),求区间和大于 \(0\) 的区间数目。这个问题可以在 \(O(n)\) 的时间内解决。
\(a\) 序列的前缀和序列为 \(s\),则当我们考虑以 \(i\) 为右端点的满足条件的区间数时,只需要知道 \(s[1..i-1]\) 中小于 \(s[i]\) 的元素的数目,把这个值记作 \(c[i]\)。而 \(s[i]\)\(s[i-1]\) 必定相差 \(1\)\(-1\) 。考虑 \(a[i]=1\) 的情形,此时 \(s[i] = s[i-1] + 1\),因此有 \(c[i]\) 等于 \(c[i-1]\) 加上 \(s[i..i-1]\)\(s[i-1]\) 出现的次数。由于 \(s[i]\) 最多有 \(O(n)\) 个不同取值,我们可以用一个数组动态维护 \(s[1..i]\) 中每个数出现的次数,这样就可以 \(O(1)\) 地由 \(c[i-1]\) 算出 \(c[i]\)

Reference

http://codeforces.com/blog/entry/18879#comment-238126

posted @ 2018-10-07 14:10  Pat  阅读(1012)  评论(0编辑  收藏  举报