Description
有两个非负整数数列,元素个数分别为N和M。从两个数列中分别任取一个数相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
时间限制为20ms
。
Input
输入文件包含三行。
第一行为三个正整数N,M和K。
第二行为N个整数,表示第一个数列。
第三行为M个整数,表述第二个数列。
Output
输出文件包含一行,一个正整数表示第K小数。
Sample Input
Sample1:
2 3 4
1 2
2 1 3
Sample2:
5 5 18
7 2 3 5 8
3 1 3 2 5
Sample Output
Sample1:
3
Sample2:
16
Hint
Source
二分 /单调性
source JZOJ 100043
鸣谢陈思宇制作数据!
Solution
我们先对于a、b数组分别排一次序,使这两个数组都具有单调性。
考虑二分答案。
假设现在二分到了mid,那么前k小的数都应满足a[i]*b[j]≤mid。
现在,我们要枚举i和j使得mid满足上述条件。
考虑如何快速地算出j。
由于a、b数组都具有了单调性,因此我们得到的j也是单调递减的,就可以利用单调性轻松求出j。
所以我们得出的算法的时间复杂度为O(N*log(最大的数)),轻松通过!
Code
1 #include <bits/stdc++.h> 2 #define int long long 3 4 using namespace std; 5 6 inline int read()//快读 7 { 8 int f = 1, x = 0; 9 char c = getchar(); 10 11 while (c < '0' || c > '9') 12 { 13 if (c == '-') 14 f = -1; 15 c = getchar(); 16 } 17 18 while (c >= '0' && c <= '9') 19 { 20 x = x * 10 + c - '0'; 21 c = getchar(); 22 } 23 24 return f * x; 25 } 26 27 int n, m, k, a[20005], b[20005]; 28 29 signed main() 30 { 31 n = read(), m = read(), k = read(); 32 33 for (register int i = 1; i <= n; i++) 34 { 35 a[i] = read(); 36 } 37 38 for (register int i = 1; i <= m; i++) 39 { 40 b[i] = read(); 41 } 42 43 sort(a + 1, a + 1 + n);//排序 44 sort(b + 1, b + 1 + m); 45 46 int l = 1, r = a[n] * b[m]; 47 48 while (l < r)//二分 49 { 50 int mid = (l + r) >> 1, sum = 0; 51 52 for (register int i = 1, j = m; i <= n; i++, sum = sum + j)//枚举i,并且计算满足条件的个数 53 { 54 while (a[i] * b[j] > mid)//如果满足条件 55 { 56 --j;//每次更新j 57 } 58 } 59 60 if (sum >= k)//如果答案比k大 61 { 62 r = mid;//缩小最大值范围 63 } 64 else 65 { 66 l = mid + 1;//否则更新最小值 67 } 68 } 69 70 printf("%lld", l);//输出符合条件的答案个数 71 72 return 0;//结束 73 }