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;
}
posted @ 2026-04-04 15:20  SHUddol  阅读(2)  评论(0)    收藏  举报