noip模拟赛 第K小数
【问题描述】
有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数
相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
【输入格式】
输入文件名为number.in。
输入文件包含三行。
第一行为三个正整数N,M和K。
第二行为N个正整数,表示第一个数列。
第三行为M个正整数,表述第二个数列。
【输出格式】
输出文件名为number.out。
输出文件包含一行,一个正整数表示第K小数。
【输入输出样例1】
number.in | number.out |
2 3 4 1 2 2 1 3 |
3 |
【输入输出样例2】
number.in | number.out |
5 5 18 7 2 3 5 8 3 1 3 2 5 |
16 |
【数据规模与约定】
样例点编号 | N | M | K | 元素大小(≤) |
1 | 20 | 20 | 150 | 10^4 |
2 | 50 | 50 | 2000 | 10^4 |
3 | 100 | 80 | 5000 | 10^9 |
4 | 200 | 200 | 26000 | 10^9 |
5 | 10000 | 10000 | 50050000 | 10^4 |
6 | 1000 | 20000 | 9500000 | 10^4 |
7 | 1000 | 20000 | 10000500 | 10^9 |
8 | 2000 | 20000 | 190000 | 10^9 |
9 | 2000 | 20000 | 199000 | 10^9 |
10 | 20000 | 20000 | 210005000 | 10^4 |
11 | 20000 | 20000 | 210000 | 10^5 |
12 | 20000 | 20000 | 200000 | 10^9 |
13 | 20000 | 20000 | 220000500 | 10^5 |
14 | 20000 | 20000 | 199000500 | 10^9 |
15 | 200000 | 200000 | 180000 | 10^4 |
16 | 200000 | 200000 | 200000 | 10^9 |
17 | 2000 | 200000 | 100001500 | 10^9 |
18 | 200000 | 180000 | 19550000000 | 10^5 |
19 | 200000 | 200000 | 19900010000 | 10^9 |
20 | 200000 | 200000 | 20000010000 | 10^9 |
分析:非常经典的一道题,要求第K小/大之类的显然可以二分,看比它小的数有多少个,统计个数主要还是乘法原理,将两个序列排序,然后移动两个指针,一个从小到大,一个从大到小,因为满足单调性,一个乘法就解决了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; ll n, m, k, a[200010], b[200010], max1, max2, ans, cnt1, cnt2; ll check(ll x) { ll cur1 = 1, cur2 = m, sum = 0; while (cur2 > 0 && cur1 <= n) { while (a[cur1] * b[cur2] > x) cur2--; sum += cur2; cur1++; } return sum; } int main() { scanf("%lld%lld%lld", &n, &m, &k); for (ll i = 1; i <= n; i++) { scanf("%lld", &a[i]); max1 = max(max1, a[i]); } for (ll i = 1; i <= m; i++) { scanf("%lld", &b[i]); max2 = max(max2, b[i]); } sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + m); ll l = 1, r = max1 * max2; while (l <= r) { ll mid = (l + r) >> 1; if (check(mid) >= k) { ans = mid; r = mid - 1; } else l = mid + 1; } printf("%lld\n", ans); return 0; }