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

Description

有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。

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

Data Constraint



二分答案$large mid$, 问题转化成判断是否有$large k$个数<=mid。

显然不能$large n^{2}$判断, 不然还不如枚举然后排序。

所以我们先把$large a$,$large b$数组从小到大排序。

然后枚举$large a$的数位置$large i$, 我们发现当$large i$递增的时候,满足$large a[i]*b[j]<=mid$的j的位置一定是单调递减的。

所以我们可以保留$large j$在上次的位置继续判断。

复杂度$large O(Nlog(maxnum))$可过。


#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define reg register
#define ll long long

inline ll read() {
    ll res=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
    return res;
}

int n, m;
ll k, ans, sum;
ll a[200005], b[200005];

inline bool check(ll mid)
{
    ll j = m;
    for (reg ll i = 1 ; i <= n ; i ++)
    {
        while (a[i] * b[j] > mid) j --;
        sum += j;
    }
    if (sum >= k) return 1;
    return 0;
}

signed main()
{
    n = read(), m = read();
    scanf("%lld", &k);
    for (reg int i = 1 ; i <= n ; i ++) a[i] = read();
    for (reg int i = 1 ; i <= m ; i ++) b[i] = read();
    sort (a + 1, a + 1 + n);
    sort (b + 1, b + 1 + m);
    ll l = 1, r = a[n] * b[m];
    while (l <= r)
    {
        ll mid = l + r >> 1;
        sum = 0;
        if (check(mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    printf("%lld
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/BriMon/p/9519221.html