CDOJ 1087 基爷的中位数 二分

16 篇文章 0 订阅

给你 N N个数, X1,X2,...,XN X1,X2,...,XN, 基爷让我们计算任意两个数差的绝对值 XiXj ∣Xi−Xj∣ (1i<jN) (1≤i<j≤N) 。 这样,我们可以得到 C2N CN2 个数。

现在,基爷希望聪明的你能用一个简单的程序求出这 C2N CN2 个数的中位数!


输入有多组数据。

每组数据,第一行一个整数 N N,第二行给出 N N 个整数 X1,X2,...,XN X1,X2,...,XN |Xi|1,000,000,000 |Xi|≤1,000,000,000; 3N100,000 3≤N≤100,000

当这 C2N CN2 个数的个数为偶数 M M 的时候,取第 M2 ⌊M2⌋ 个最小的数作为中位数 ( 别问为什么,这就是 基爷的中位数! )

刚开始想了好久也没明白怎么二分,然后后来还是看了看别人的代码- -(但是这次没有细看),又仔细地想,就想出来了

先把x序列sort一下

我们可以先找到中位数的位置,从1开始计数,第n*(n-1)/2就是所求的中位数,假设这个中位数是x。
当然,我们就是要求这个x是多少,所以可以二分枚举。
对于区间[l,r],算mid在第几位上,加入mid所在的位数太靠后,就是[l,mid],太靠前,就是[mid+1,r]。
我的思路就是,对于这个mid,算所有的<=mid的数共有多少个,假设有y个,我只需要y>=mid的这个最小的mid,就是要求的中位数。
然后怎么算它在第几位呢,我的算法就是所有差值<mid+1的,枚举每个ai为xi,然后后面所有的为xj,这个差值肯定是递增的,然后lower_bound找到mid+1所在的位置就行,然后枚举完,也就算完了

这个是nloglog的做法,貌似还有nlog的,今天就先不研究了。

具体看代码吧:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100004
long long num[maxn];
int main()
{
	//freopen("input.txt", "r", stdin);
	long long n;
	while (scanf("%lld", &n) != EOF)
	{
		for (int i = 0; i < n; ++i)
			scanf("%lld", &num[i]);
		sort(num, num + n);
		long long pos = n*(n - 1) / 2;
		pos = (pos + 1) >> 1;
		long long l = 0, r = 2000000005, mid, ans = -1;
		while (l < r)
		{
			mid = (l + r) >> 1;
			long long tmp = 0;
			for (int i = 0; i < n; ++i)
			{
				tmp += lower_bound(num + i + 1, num + n, num[i] + mid + 1) - num - i - 1;
				//printf("%lld\n", tmp);
			}
			if (tmp >= pos)
				r = mid;
			else
				l = mid + 1;
		}
		printf("%lld\n", r);
	}
	//system("pause");
	//while (1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值