51Nod1105 第K大的数

题目链接

题目描述

数组A和数组B,里面都有n个整数。
数组C共有n^2个整数,分别是:
A[0] * B[0],A[0] * B[1] ...... A[0] * B[n-1]
A[1] * B[0],A[1] * B[1] ...... A[1] * B[n-1] 
......
A[n - 1] * B[0],A[n - 1] * B[1]  ......  A[n - 1] * B[n - 1]
是数组A同数组B的组合,求数组C中第K大的数。
例如:
A:1 2 3,B:2 3 4。
A与B组合成的C为
         A[0]  A[1]  A[2]
B[0]     2      3      4
B[1]     4      6      8
B[2]     6      9     12
共9个数。

输入输出格式

输入格式:

第1行:2个数N和K,中间用空格分隔。N为数组的长度,K对应第K大的数。
第2 - N + 1行:每行2个数,分别是A[i]和B[i]。

输出格式:

输出第K大的数。

数据范围:

(2 <= N <= 50000,1 <= K <= 10^9)
(1 <= A[i],B[i] <= 10^9)

样例

样例一输入:

3 2
1 2
2 3
3 4

样例一输出:

9

思路

1.二分

将A数组升序排序,不难发现, $ B_{x} ast A_{i} $ 为升序序列
那么首先二分第K大的值为h,然后对于序列 $ B_{x} ast A_{i} $ ,二分其中有多少个数小于h
复杂度 $ Theta left( nlog nlog max left( Aleft[ i ight] ast Bleft[ i ight] ight) ight) $
还有个小优化
考虑将A,B都升序排序
那么序列 $ B_{x} ast A_{i} $ 和 $ A_{x} ast B_{i} $ 都为升序序列
所以在二分小于h的个数时,第m次二分最大的边界不会超过第m-1次二分的答案
维护边界变量即可

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <list>
#include <map>

#define ll long long
#define ull unsigned long long

using namespace std;

int n;
ull a[50050],b[50050];
ull p,k;

bool check(ull mid) {
    ull sum = 0;
    ull l = 0, r = n, tmp;
    for(int i = 1; i <= n; i++) {
        if(r == 0)break;
        while(l <= r) {
            ull m = (l + r) >> 1;
            if(a[i] * b[m] < mid) {
                tmp = m;
                l = m + 1;
            } else r = m - 1;
        }
        sum += tmp;
        l = 0;
    }
    return sum <= p-k;
}

int main(int argc, char const *argv[])
{
    scanf("%d%llu",&n,&k);
    p = (ull)n * n;
    for(int i = 1; i <= n; i++) {
        scanf("%llu%llu",&a[i],&b[i]);
    }
    sort(a+1,a+1+n);
    sort(b+1,b+1+n);
    ull l = a[1]*b[1], r = (ull)a[n]*b[n];
    ull ans;
    while(l <= r) {
        ull mid = (l+r) >> 1;
        if(check(mid)) {
            ans = mid;
            l = mid + 1;
        } else r = mid - 1;
    }
    printf("%llu",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ullio/p/9823597.html