emmm……
不要在意这不对劲的日期(我只是把忘记写了的补回来)
【题目】
题目描述:
有两个正整数数列,元素个数分别为 N 和 M 。从两个数列中分别任取一个数相乘,这样一共可以得到 N*M 个数,询问这 N*M 个数中第 K 小数是多少。
输入格式:
第一行为三个正整数 N,M 和 K 。第二行为 N 个正整数,表示第一个数列。
第三行为 M 个正整数,表述第二个数列。
输出格式:
输出包含一行,一个正整数表示第 K 小数。
样例数据:
输入:
2 3 4
1 2
2 1 3
输出:
3
输入
5 5 18
7 2 3 5 8
3 1 3 2 5
输出
16
备注:
数据规模与约定:
【分析】
看到题我还觉得简单,但一看到数据范围还是把我吓了一跳,果然现在第1题都这么吓人了吗
最后在考场上我还是打了个暴力,结果不知道为什么只过了10分,心塞
这道题正解的做法是二分,其实我也想过二分,但不知道怎么判断二分出的答案,就只好作罢
先对两个数列排序(假设两个数列分别为a,b),则下界为a[1]*b[1],上界为a[n]*b[m]
然后二分答案,判断一下这个答案在k个数之后,然后不断缩小范围,直到找到答案
/********************************************************************************************************/
至于怎么判断二分到的答案是否合法呢,我们先枚举a数组中的每一个数,用此时的答案除以a[i]得到一个值(记做num),又枚举b数组中的数,若b[j]小于num,那就说明此时a[i]*b[j]是小于二分到的值的,统计一下这些数的个数,再与K比较即可,由于b数组是排过序的,所以从后往前扫一遍就可以了(从前往后扫也可以)
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
int n,m;
long long k;
int a[N],b[N];
bool check(long long x)
{
int i,top=m;
long long num,order=0;
for(i=1;i<=n;++i)
{
num=x/a[i];
while(top&&b[top]>num)
top--;
order+=top;
}
return order>=k;
}
int main()
{
// freopen("number.in","r",stdin);
// freopen("number.out","w",stdout);
int i;
long long l,r,mid;
scanf("%d%d",&n,&m);
scanf("%lld",&k);
for(i=1;i<=n;++i) scanf("%d",&a[i]);
for(i=1;i<=m;++i) scanf("%d",&b[i]);
sort(a+1,a+n+1);
sort(b+1,b+m+1);
l=1ll*a[1]*b[1];
r=1ll*a[n]*b[m];
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%lld",l);
// fclose(stdin);
// fclose(stdout);
return 0;
}