中石油4875 第k大数(二分)


4875: 第k大数

时间限制: 10 Sec  内存限制: 128 MB
提交: 80  解决: 28
[ 提交][ 状态][ 讨论版]

题目描述

有两个序列a,b,它们的长度分别为n和m,那么将两个序列中的元素对应相乘后得到的n*m个元素从大到小排列后的第k个元素是什么?

输入

输入的第一行为一个正整数T (T<=10),代表一共有T组测试数据。

每组测试数据的第一行有三个正整数n,m和k(1<=n, m<=100000,1<=k<=n*m),分别代表a序列的长度,b序列的长度,以及所求元素的下标。第二行为n个正整数代表序列a。第三行为m个正整数代表序列b。序列中所有元素的大小满足[1,100000]。

输出

对于每组测试数据,输出一行包含一个整数代表第k大的元素是多少。

样例输入

33 2 31 2 31 22 2 11 11 12 2 41 11 1

样例输出

311

题解:暴力的话,10s也会超时,想到用二分,二分的区间是谁,因为a和b序列之间的数字是无序的相乘也是无序,但是我们可以对a和b序列产生的最小值到最大值查找,进行二分,判断比这个数大的数字有多少个与k比较,但是当找到一个数,比它大的数字的个数正好为k个的话,要考虑当前的这个数字,是不是a和b序列产生的,可能这个数字不存在。

每次二分都想不到,只能多做多想了。

代码:


#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
ll a[N],b[N];
int n, m;
int cmp(int c, int d) {
    return c > d;
}

ll judge(ll mid) {
    ll sum = 0;
    int j = m-1;
    for(int i = 0; i < n; i++)
        for(; j >=0; j--)
            if(a[i]*b[j] >= mid) {
                sum += j+1;
                break;
            }

    return sum;
}

int main() {
    int t;
    ll k;
    scanf("%d", &t);

    while(t--) {
        scanf("%d%d%lld", &n, &m, &k);
        for(int i = 0; i < n; i++)
            scanf("%lld", &a[i]);
        for(int i = 0; i < m; i++)
            scanf("%lld", &b[i]);
        sort(a, a+n, cmp);
        sort(b, b+m, cmp);

        ll l = a[n-1]*b[m-1];
        ll r = a[0]*b[0];

        while(l<=r) {
            ll mid = (l+r)/2;
            if(judge(mid) >= k)
                l = mid+1;
            else
                r = mid-1;
        }

        printf("%lld\n", l-1);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值