第k大数
题目描述
有两个序列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大的元素是多少。
样例输入
3 2 3
1 2 3
1 2
2 2 1
1 1
1 1
2 2 4
1 1
1 1
样例输出
1
1
二分搜索:
最大边界:arr1[n - 1] * arr2[m - 1],最小边界arr1[0] * arr2[0](排序后)
AC Code:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <queue>
#include <climits>
#include <set>
#include <stack>
#include <string>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
static const int MAX_N = 1e5 + 5;
int arr1[MAX_N], arr2[MAX_N]; //数组就开int,开ll输入会超时
ll get_sum(ll v, int n, int m) { //大于v的个数
ll ret = 0;
for (int i = 0; i < n; ++i) {
ret += (m - (upper_bound(arr2, arr2 + m, v / arr1[i]) - arr2));
}
return ret;
}
int main() {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < n; ++i) scanf("%d", &arr1[i]);
for (int i = 0; i < m; ++i) scanf("%d", &arr2[i]);
sort(arr1, arr1 + n);
sort(arr2, arr2 + m);
ll r = (ll)arr1[n - 1] * arr2[m - 1], l = (ll)arr1[0] * arr2[0];
while (l <= r) { //需要取=,这里是二分搜索边界内的值
ll mid = (l + r) >> 1;
if (get_sum(mid, n, m) > k - 1) l = mid + 1;
else r = mid - 1;
}
printf("%lld\n", l);
}
return 0;
}