CSU1928 - 又是第K大(二分答案)

今天学弟问了这个题目,自己也是做了两个小时, debug d e b u g 就占了一个半小时,后来发现数据类型不能用int(因为这个WA了无数次),后来抱着试一试的心态试了下long long,发现过了!简直不敢相信。题目保证了 1kmin(2.5×109,109) 1 ≤ k ≤ m i n ( 2.5 × 10 9 , 10 9 ) ,但是,在二分答案的统计过程中可能把C数组中所有的元素统计完,因此,这个个数就可能是 2.5×109 2.5 × 10 9 ,这里超了int的最大范围。最后才发现。。。

题目链接:点这儿。

题目

给出两个长度均为 n n 的数组 A A B B 。而 C C 数组是 A A B B 合成的,长度为 nn n ∗ n ,并且满足等式 c[(j1)n+k]=a[j]+b[k](1<=jk<=n) c [ ( j − 1 ) ∗ n + k ] = a [ j ] + b [ k ] ( 1 <= j 、 k <= n )
其实就是说 C C 中的元素分别是 A[1]+B[1]A[1]+B[2]A[1]+B[n]A[2]+B[1]A[2]+B[n]A[n]+B[n] A [ 1 ] + B [ 1 ] 、 A [ 1 ] + B [ 2 ] 、 ⋯ 、 A [ 1 ] + B [ n ] 、 A [ 2 ] + B [ 1 ] 、 ⋯ 、 A [ 2 ] + B [ n ] 、 ⋯ 、 A [ n ] + B [ n ]
C C 中第 K K 大的元素的值(从小到大排序后的第 k k 个)。

输入描述

输入包括不超过15组数据
第一行有两个数,分别为 A A B B 的长度 n n 以及 k k (1<=n<=50000,1<=k<=min(nn,1000000000)) ( 1 <= n <= 50000 , 1 <= k <= m i n ( n ∗ n , 1000000000 ) )
第二行 n n 个值为数组 A A (1<=A[i]<=50000) ( 1 <= A [ i ] <= 50000 )
第三行 n n 个值为数组 B B (0<=B[i]<=50000) ( 0 <= B [ i ] <= 50000 )

输出描述

输出一行,为 C C 数组中第 K K 大的数

样例

in:
3 4
1 2 3
4 5 6

out:
7

解析

首先,想要把 C C 数组计算出来然后排序是肯定不行的。

这种要求数组中第 K K 大元素的题,一般都可以考虑计数,然后二分,因此就可以二分答案。

二分这个第 K K 大的数的值,然后去看看 C C 数组中有多少个数字是小于这个值的(如何做呢,可以枚举 A A 数组中的元素,看看 B B 数组中有多少个元素是小于 xA[i] x − A [ i ] 的,这里先把 B B 数组排序,二分upper_bound查找就行了),我们找到第一个大于等于 K K 的那个值。这是一个求下界的二分,可以参考这篇博客
你真的理解二分的写法吗 - 二分写法详解。

时间复杂度: O(nlogn×logm) O ( n l o g n × l o g m )

代码

#include 

#include 
#include 

using namespace std;

typedef long long LL;

int main()
{
    for (LL n, k; EOF != scanf("%lld%lld", &n, &k); ) {
        vector<int> a(n), b(n);
        for (int i = 0; i < n; scanf("%d", &a[i++])) {}
        for (int i = 0; i < n; scanf("%d", &b[i++])) {}
        sort(a.begin(), a.end());
        sort(b.begin(), b.end());

        LL l = a.front() + b.front(), r = a.back() + b.back() + 1;
        struct {
            bool operator () (vector<int> &a, vector<int> &b, const LL k, const LL x) {
                LL sum = 0;
                for (auto it = a.begin(); it != a.end(); ++it)
                    sum += upper_bound(b.begin(), b.end(), x - *it) - b.begin();
                return sum < k;
            }
        } judge;
        while (l < r) {
            LL mid = (l + r) / 2;
            if (judge(a, b, k, mid))
                l = mid + 1;
            else
                r = mid;
        }
        printf("%lld\n", l);
    }
    return 0;
}

你可能感兴趣的:(OJ)