题目描述
慈溪小学编程复赛马上就要到来了,小 A 和小 B 正在做最后的准备工作。
老师最近给小 A 和小 B 考了这样一个问题:有 N 个正整数 a1, a2, …, aN , 对这些正整数任意 2 个做差以后求绝对值。例如,我们对 ai 和 aj 作差,可以得到 |ai − aj|。
显然,这样的差一共有 N(N−1) /2个。
我们令 M = N(N−1)/2 ,输入的 N 保证 M 一定是一个偶数。老师现在想让小 A 和 小 B 求出所有这些绝对值里面从小到大第 M/2小的数。
小 A 和小 B 想了很久,想请你来帮助他们。
注意:在数学中,我们规定一个负数的绝对值就是把它的负号去掉以后的数,例如,| -5| = 5, 而 0 和正数的绝对值还是它本身。
输入
输入的第一行是一个正整数 N,表示数的个数。接下来 N 行,每行一个正整数,表示 ai。
输出
输出所有绝对值中从小到大第 M/2 小的数。
样例
样例输入
4
1
10
2
3
样例输出
2
提示
【样例解释】
样例中,我们总共有 4 个数,求出来的绝对值分别是:|1 − 10| = 9, |1 − 2| = 1, |1 − 3| = 2, |10 − 2| = 8, |10 − 3| = 7, |2 − 3| = 1,从小到大排序以后就是 1,1,2,7,8,9,M 是 3。所以第 M 小的数是 2。
【数据范围】
对于所有的数据,保证 1 ≤ N ≤ 1e5, 1 ≤ ai ≤ 1e9。
分析
N 个正整数 a1, a2, …, aN , 对这些正整数任意 2 个做差以后求绝对值。例如,我们对 ai 和 aj 作差,可以得到 |ai − aj|。
差一共有 N(N−1) /2个,求绝对值里从小到大的第 N(N−1) /4 小 的数
要找绝对值里从小到大的第 N(N−1) /4 小的数,可以考虑 二分
直接二分答案,考虑check函数怎么写
假设二分出来的是x,核心是判断比x小于等于的绝对值有多少个(跟m的关系)
先把原数组从小到大排序,则所有的绝对值可以用(l,r)表示出来,其中l < r
枚举i,对于i,a[j] - a[i] 是单调的(j > i)
所以,最后一个 <= a[i] + x 的数 即为 与a[i]的差的绝对值 <= x的最后一个数,
设下标为pos,则 l= i 时对答案的贡献即为 pos - i
最终判断是否 >= m 即可
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n,m;
int a[N];
bool check(int x){
LL cnt = 0;
for(int i = 0;i < n;i++){
int pos = upper_bound(a,a + n,a[i] + x) - a - 1;
cnt += pos - i;
}
return cnt >= m;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> n;
for(int i = 0;i < n;i++) cin >> a[i];
sort(a,a + n);
m = (LL)n * (n - 1) / 2 / 2;
// cout << m << endl;
int l = 0,r = a[n - 1];
while(l < r){
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
// cout << l << ' ' << r << endl;
}
cout << l;
return 0;
}