【poj3065改编】第K小数

【问题描述】

  给出 n 个整数:X1, X2, … , XN,定义f(i,j)=∣Xi - Xj∣ (1 ≤ i < j ≤ n),于是我们可以得到C(n,2)个f(i,j)。(注意C(n,2)表示从x1…xn中取两个元素的组合数)

  你的任务是找出这C(n,2)个f(i,j)中第k小的数(把f(i,j)由小到大排序后的第k个数)。

【输入格式】

  包含多组测试数据,每组数据占两行,第一行为整数n 和 k,第二行包含n个整数,表示X1, X2, … , XN, ( Xi ≤ 1,000,000,000 3 ≤ n ≤ 100,000 )

【输出格式】

  对于每组数据,输出一行一个整数,表示第k小的数。

【输入样例】

4 3
1 3 2 4
3 2
1 10 2

【输出样例】

1
8

【样例解释】

样例中的第一组数据:1 3 2 4,得到f(i,j)序列为:2 1 3 1 1 2,其中第3小的是1。

【数据范围】

|Xi| ≤ 1,000,000,000 , 3 ≤ n ≤ 1,00,000 , 1 ≤ k ≤ C(n,2)

题目大意:给你一个数列x,要求你求出第k小的|xi-xj|。
题目乍看上去不难,我们可以两重循环生成出所有的|xi-xj|并把他们放入一个数组(或者集合之类的容器)中,然后排序取第k个元素就是答案。
但是本题n规模很大,可达到10000,C(10000,2)=9999*10000/2=49995000,也就是说你最后算出的那个数组有接近五千万个元素,而且转存的时候时间也不堪忍受。
优化解法:先将原来的数列从小到大排序。当j>i时,有xj>xi,|xj-xi|=xj-xi;
这时统计|xj-xi|小于m的个数cnt,和k比较,如果说cnt小于k,则说明m<第k小的数ans,向更大的区间猜,由于该不等式有两个未知的元素,所以说循环其中一个元素(以循环j为例),在x1…xj-1中二分查找第一个大于等于xj-m的元素下标p,由于排序后的数组的单调性特点,所以说在xp…xj-1间的元素都满足|xj-xi|小于m。
总的时间复杂度:O(n*log(n)*log(max)) 其中max为最大的f(i,j)。

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
#define maxn 100005
using namespace std;
typedef long long LL;
const int inf=1000000000;
int n;
LL k;
int a[maxn];

bool check(int mid)
{
    LL cnt=0;
    for(int j=2;j<=n;j++)
    {
        int p=lower_bound(a+1,a+j,a[j]-mid)-a;
        cnt+=j-p;
    }
    if(cnt<k)return 0;
    return 1;
}

void solve()
{
    int A=0,B=inf,Ans;
    for(int i=0;i<50;i++)
    {
        int m=A+B>>1;
        if(check(m))
        B=m,Ans=m;
        else
        A=m;    
    }
    printf("%d\n",Ans);
}
int main()
{
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);

    while(scanf("%d",&n)==1)
    {
        cin>>k;
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);

        sort(a+1,a+n+1);

        solve();
    }

    return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值