2018.07.08 NOIP模拟 第K小数

第K小数
题目背景
SOURCE:NOIP2016-AHSDFZ T1
题目描述
有两个正整数数列,元素个数分别为 N 和 M 。从两个数列中分别任取一个数相乘,这样一共可以得到 N*M 个数,询问这 N*M 个数中第 K 小数是多少。
输入格式
第一行为三个正整数 N,M 和 K 。
第二行为 N 个正整数,表示第一个数列。
第三行为 M 个正整数,表述第二个数列。
输出格式
输出包含一行,一个正整数表示第 K 小数。
样例数据 1
输入 
2 3 4
1 2
2 1 3
输出
3
样例数据 2
输入 
5 5 18
7 2 3 5 8
3 1 3 2 5
输出
16
备注
【数据规模与约定】

这里写图片描述

这次考试差点爆 0 (惊了*1),这道一(简)眼(单)题考试竟然写挂了, 30 分滚粗,下来后听说我的方法可以过 70 (惊了*2),然后 讲讲这道题怎么做吧。

70 算法很好想,二分第 k 大取值,然后用 u p p e r b o u n d 统计 m i d 大的数的个数,然后就 70 分了。

然而这个算法的时间效率并不优秀,我们要想办法去掉一个 l o g ,观察到 c h e c k 时随着一个数组中的值的增大,另一个数列中可以满足 a i b j 成立的 j 的个数单调不升,这样的话我们没必要用 u p p e r b o u n d ,直接利用单调性统计就行了。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
int n,m,a[N],b[N];
ll k;
inline ll read(){
    ll ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
    return ans;
}
inline ll check(ll k){
    ll up=m,cnt=0;
    for(int i=1;i<=n;++i){
        ll v=k/a[i];
        while(up&&b[up]>v)--up;
        cnt+=up;
    }
    return cnt;
}
int main(){
    n=read(),m=read(),k=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=m;++i)b[i]=read();
    sort(a+1,a+n+1),sort(b+1,b+m+1);
    ll l=a[1]*(ll)b[1],r=a[n]*(ll)b[m];
    while(l<r){
        ll mid=l+r>>1;
        if(check(mid)>=k)r=mid;
        else l=mid+1;
    }
    printf("%lld",l);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/80964102