求中位数【二分】

>Link

ybtoj求中位数


>Description

给定 n 个数,求这 n 个数两两的差值(共 n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n(n1) 个)的中位数。

n ≤ 2 ∗ 1 0 5 , a i ≤ 2 31 − 1 n \le 2*10^5,a_i\le2^{31}-1 n2105,ai2311


>解题思路

考虑二分答案
二分一个中位数 x x x,我们判断有多少差值小于等于 x x x
可以先对 a a a 排个序,然后对于每一个数,往后找差值小于等于 x x x 的数有多少个,同样二分解决
时间复杂度 O ( 31 ∗ n l o g n ) O(31*nlogn) O(31nlogn)

之前我打二分都是ans取mid的min值,不知道为什么这道题不行(改了好久TT),要直接把mid赋值给ans


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
#define inf 20000000
#define LL long long
using namespace std;

LL n, m, pos, a[N], ans, ans1, ans2;

bool check (LL x)
{
    
    
	LL cnt = 0, p;
	for (LL i = 1; i <= n; i++)
	{
    
    
		p = upper_bound (a + 1, a + 1 + n, a[i] + x) - a - 1;
		if (i < p && p <= n) cnt += p - i;
		if (cnt >= pos) return 1;
	}
	if (cnt >= pos) return 1;
	return 0;
}

int main()
{
    
    
	freopen ("mid.in", "r", stdin);
	freopen ("mid.out", "w", stdout);
	LL l, r, mid;
	scanf ("%lld", &n);
	m = n * (n - (LL)1) / (LL)2;
	for (LL i = 1; i <= n; i++) scanf ("%lld", &a[i]);
	sort (a + 1, a + 1 + n);
	if (m & 1)
	{
    
    
		pos = m / (LL)2 + (LL)1;
		l = 0, r = a[n];
		while (l <= r)
		{
    
    
			mid = (l + r) / (LL)2;
			if (check (mid)) ans = mid, r = mid - 1;
			else l = mid + 1; 
		}
		printf ("%lld", ans);
	}
	else
	{
    
    
		pos = m / (LL)2;
		l = 0, r = a[n];
		while (l <= r)
		{
    
    
			mid = (l + r) / (LL)2;
			if (check (mid)) ans1 = mid, r = mid - 1;
			else l = mid + 1; 
		}
		pos = m / (LL)2 + (LL)1;
		l = 0, r = a[n];
		while (l <= r)
		{
    
    
			mid = (l + r) / (LL)2;
			if (check (mid)) ans2 = mid, r = mid - 1;
			else l = mid + 1;
		}
		printf ("%lld", (ans1 + ans2) / (LL)2);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/121181627