2018/07/07测试T1 第K小数

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;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/81008376
今日推荐