题目
给定一组数 X i X_i Xi,我们可以得到 C ( n , 2 ) C(n,2) C(n,2)个差值 ∣ X i − X j ∣ ( i < j ) |X_i-X_j|(i < j) ∣Xi−Xj∣(i<j),问求这些差值组成的数列中第 k k k个 ( k = C ( n , 2 ) / 2 ) (k=C(n,2)/2) (k=C(n,2)/2)是多少。
思路
比较常见的二分题目。
首先对输入的数据从小到大排序,然后在
[
0
,
X
[
n
−
1
]
−
X
[
0
]
]
[0, X[n-1]-X[0]]
[0,X[n−1]−X[0]]这个范围内查找。
1)找到要check的差值
m
i
d
mid
mid
2)判断数组中大于等于这个差值的有多少元素:对于每一个元素
i
i
i,我们可以知道所有在
[
X
[
i
]
+
m
i
d
,
X
[
n
−
1
]
]
[X[i]+mid, X[n-1]]
[X[i]+mid,X[n−1]]范围内元素的个数就是差值大于mid的元素个数。可以直接通过lower_bound函数得到,然后累加起来,就可以得出结果。
3)如果元素个数比
k
k
k大,说明
m
i
d
mid
mid的值太小,提高下界
如果元素个数比
k
k
k小,减小上界。
如果等于怎么办,等于说明差值mid位于第
k
k
k个和
k
+
1
k+1
k+1个之间,我们应该向左逼近,所以把等号放在第二种情况(减少上界)。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int max_n = 1e6;
ll a[max_n+10];
int n, k;
int getNums(ll md) {
// 返回比差值比md大的元素个数
int acc = 0;
for (int i = 0; i < n; i++) {
acc += n-(lower_bound(a, a+n, a[i]+md)-a);
}
return acc;
}
int main() {
while (~scanf("%d", &n)) {
for (int i = 0; i < n; i++)
scanf("%lld", &a[i]);
sort(a, a + n);
k = (n*(n-1)/2)/2;
ll lo = 0, hi = a[n-1]-a[0];
ll res = 0;
while (lo <= hi) {
ll mid = lo + (hi-lo)/2;
int m = getNums(mid);
if (m > k) {
lo = mid + 1;
res = mid;
}
else
hi = mid - 1;
}
printf("%lld\n", res);
}
return 0;
}