有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
1<=n,m<= 2∗105 , 1<=k<=n*m
先将两个数组排序。
二分答案Ans。
对于a[i]找到最大的j使得a[i]*b[j]<=Ans。
那么对于a[i+1]的j一定小于对于a[i]的j,这个我们暴力扫一遍,就是O(n+m)的。
总复杂度是 O((n+m)∗log10182)
考场时我没有想到这样的做法,我想到的是O(n log n log m)的,然后也加了超级多的优化,直接过了。
Code:
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int Maxn = 200005;
int n, m, v, a[Maxn], b[Maxn], c[Maxn], d[Maxn], e[Maxn];
ll u, ans, ma, mb;
void read(int &x) {
char ch = ' '; for(;ch < '0' || ch > '9'; ch = getchar());
x = 0; for(;ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
}
bool pd(ll x) {
ll s = 0;
int rr = m;
fo(i, 1, n) {
ll p = x / a[i]; e[i] = 0;
for(int l = c[i], r = min(rr, d[i]); l <= r; ) {
int m = (l + r) / 2;
if(b[m] <= p) e[i] = m, l = m + 1; else r = m - 1;
}
s += e[i];
rr = e[i];
if(s >= u) {
v = i;
return 1;
}
}
return 0;
}
int main() {
read(n); read(m); scanf("%lld", &u);
fo(i, 1, n) read(a[i]), ma = max(ma, a[i]);
fo(i, 1, m) read(b[i]), mb = max(mb, b[i]);
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
fo(i, 1, n) c[i] = 1, d[i] = m;
for(ll l = 1, r = ma * mb; l <= r;) {
ll m = (l + r) / 2;
if(pd(m)) {
ans = m, r = m - 1;
fo(i, 1, v) d[i] = min(d[i], e[i]);
} else {
l = m + 1;
fo(i, 1, v) c[i] = max(c[i], e[i]);
}
}
printf("%lld\n", ans);
}