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

本文探讨了如何从两个已排序的整数数组中找出所有可能的乘积中第K小的数。通过使用二分查找技巧,在O(n)的时间复杂度内解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

分析

我们有两个数值a[1..n],b[1..m]
如果将它们排序之后,最小的数一定是a1b1
考虑第2小的数,有可能是a2b1a1b2
看来这种方法并不是很理想。

换一种思路,我们想到了二分。
我们可以二分一个最大值,然后判断一下小于等于这个数的数的个数是否达到k个。
现在问题就变为了求有多少个数小于等于k。

对于同一行的数一定是递增的,可以用二分找,
但是这样就有两log,过不了的。

从上一行到下面的一行,所选到的最后一个位置一定是小于等于上一行的最后一个位置。
这样就可以O(n)找了。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define N 200003
#define ll long long
using namespace std;
int n,m,x,y,tot,f[N];
ll l,r,mid,k,a[N],b[N],sum,p,w[N],mx;
char ch;
void read(ll &n)
{
    n=0;
    ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-')ch=getchar();
    int w=1;
    if(ch=='-')w=-1,ch=getchar();
    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();
    n*=w;
}
int ef(int l,int r,int x,ll mx)
{
    int mid;
    while(l<r)
    {
        mid=(l+r)/2;
        if(a[x]*b[mid]>=mx)r=mid;else l=mid+1;
    }
    if(a[x]*b[l]>mx)return l-1;
    return l;
}
int main()
{
    scanf("%d%d",&n,&m);read(k);
    for(int i=1;i<=n;i++)
        read(a[i]);
    for(int i=1;i<=m;i++)
        read(b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+1+m);

    l=1;r=a[n]*b[m];
    while(l<r)
    {
        mid=(l+r)/2;
        sum=0;
        x=m;
        for(int i=1;i<=n;i++)
        {
            while(a[i]*b[x]>mid)x--;
            sum+=x;
        }
        if(sum>=k)r=mid;else l=mid+1;
    }
    printf("%lld",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值