POJ3579 Median 题解
Description
给定 \(N\) 个数 \(X_1, X_2, \dots, X_N\),计算每一对数差的绝对值:\(\lvert X_i - X_j \rvert\),其中 \(1 \le i < j \le N\)。 这样一共可以得到 \(\dbinom{N}{2}\) 个差值。现在你的任务是尽快找出这些差值的中位数。
注意:在本问题中,如果差值的个数 \(m\) 是偶数,则中位数定义为第 \(\dfrac{m}{2}\) 小的数。 例如,当 \(m = 6\) 时,你需要找出第 \(3\) 小的差值作为中位数。
数据范围:\(X_i \le 10^9\),\(3 \le N \le 10^5\)。
Solution
这些差值的中位数 \(\text{Med}\) 就是这些差值中第 \(\left \lfloor \dfrac{m + 1}{2} \right \rfloor\) 小的数。
考虑任一小于 \(\text{Med}\) 的数 \(p\),差值中所有 \(\le p\) 的数至多不会有 \(\left \lfloor \dfrac{m + 1}{2} \right \rfloor\) 个。
考虑任一大于等于 \(\text{Med}\) 的数 \(q\),差值中所有 \(\le q\) 的数至少有 \(\left \lfloor \dfrac{m + 1}{2} \right \rfloor\) 个。
于是考虑二分法,每次令 \(\text{mid} = \left\lfloor\dfrac{l + r}{2}\right\rfloor\),然后计算 \(\le \text{mid}\) 的差值的个数 \(\text{cnt}\)。
- 如果 \(\text{cnt} \ge \left \lfloor \dfrac{m + 1}{2} \right \rfloor\),那么 \(\text{mid} \ge \text{Med}\),于是 \(r \leftarrow \text{mid}\);
- 如果 \(\text{cnt} < \left \lfloor \dfrac{m + 1}{2} \right \rfloor\),那么 \(\text{mid} < \text{Med}\),于是 \(l \leftarrow \text{mid} + 1\)。
我们使用双指针法来计算 \(\text{cnt}\),每次计算代价为 \(O(N)\)。
总复杂度 \(O(N \log N)\) 。
Code
#include <bits/stdc++.h>
typedef long long i64;
using namespace std;
const int MAXN = 1e5;
int n, x[MAXN + 5];
i64 cal(int mid) {
int j = 0;
i64 cnt = 0;
for (int i = 0; i < n; ++i) {
while (j < n && x[j] - x[i] <= mid) ++j;
cnt += j - i - 1;
}
return cnt;
}
void solve() {
for (int i = 0; i < n; ++i)
scanf("%d", x + i);
sort(x, x + n);
i64 t = (i64(n) * (n - 1) / 2 + 1) / 2;
int l = 0, r = x[n - 1] - x[0];
while (l < r) {
int mid = (l + r) >> 1;
if (cal(mid) >= t)
r = mid;
else
l = mid + 1;
}
printf("%d\n", l);
}
int main() {
while (~scanf("%d", &n)) solve();
return 0;
}

浙公网安备 33010602011771号