【NOIP2017提高A组模拟7.13】第K小数

题目大意:

有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
1<=n,m<= 2105 , 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);
}

你可能感兴趣的:(杂题)