题目大意:给出n个数,要求将这n个数两两相减,把这些相减得到的数排序后,输出位置在中间的那个数
解题思路:如果两两相减再排序复杂度太高,肯定超时了,不妨换另一种思路
枚举最中间的那个数,然后判断一下相减得到的数有多少个大于等于枚举的数
如何判断上面所说的那句呢,其实不用把每个数相减,只需要排序一下,然后将当前这个数 + 枚举的那个数,然后在数组中找到大于等于这个数的第一个位置(lower_bound()),再用n减去那个数的位置就可以知道有多少个相减的结果会大于等于这个数了
最后只需要判断一下大于等于这个枚举数的数有几个就可以了,当这个数大于等于(n * (n - 1) / 2 / 2 - 1)就表示这个枚举数是小于或者等于这个最中间值的(等于的情况就是最中间值能有多个数相减得到)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 100010
#define INF 0x3f3f3f3f
int n;
ll Max, num[maxn];
int low_bound(int l, int r, ll Num) {
int L = l, R = r;
while(L <= R) {
int mid = (L + R) / 2;
if(num[mid] < Num)
L = mid + 1;
else
R = mid - 1;
}
return L;
}
bool judge(ll mid) {
ll cnt = 0;
for(int i = 0; i < n; i++) {
//cnt += (num + n) - lower_bound(num + i, num + n, num[i] + mid);
cnt += n - low_bound(i,n-1,num[i] + mid);
}
ll m = (ll)n * (n - 1) / 2;
return cnt >= (m / 2 + 1);
}
ll solve() {
ll l = 0, r = Max;
while(l <= r) {
ll mid = (l + r) / 2;
if(judge(mid))
l = mid + 1;
else
r = mid - 1;
}
return l - 1;
}
int main(){
while(scanf("%d", &n) != EOF) {
Max = -INF;
for(int i = 0; i < n; i++) {
scanf("%lld", &num[i]);
Max = max(num[i], Max);
}
sort(num, num + n);
printf("%lld\n", solve());
}
return 0;
}
时间: 2024-10-15 04:02:15