Description
现在有两个数组 A 和 B, 分别包含 x 与 y 个元素。
定义一个新的数组 C, C 中包含 x×y 个元素,为 A 中所有元素除以 B 中所有元素。
即 新集合为 {c∣c=ab,a∈A,b∈B} 。特殊地,C 为多重集合。
请求 C 数组的第 k 大数。
Input
第一行一个整数 T(T≤3)表示方案数。
对于每个方案:
第一行三个整数 n,m,k(0<n,m≤100000,0<k≤n×m )
第二行 n 个正整数;
第三行 m 个正整数。
数组中元素 <108。
Output
对于每个方案,输出一行:
数组 C 的第 k 大数。结果四舍五入到两位小数。
Sample Input
2
5 5 3
1 2 3 4 5
2 3 4 5 6
5 5 2
1 2 3 4 5
2 3 4 5 6
Sample Output
1.67
2.00
题解:这个题乍一看想FFT,其实没有那么麻烦,只是个简单的二分答案。
如果一个数是第K大的数,那么一定存在至少K个数小于等于它,把这个作为二分的条件。
如何判断有K个数小于等于它呢,好像不是很好判断,我们转换为计算大于x的数的个数。
我们可以把A数组和B数组排序,然后用双指针,指针Pa指向数组A的左端,指针Pb指向数组B的左端,这样固定Pa,把Pb像右移动,直到(*Pa)/(*Pb) < x为止,然后Pa向右移动一个,再循环这个操作,直到Pa到头或者Pb到头(可以证明Pb始终不需要掉头)
check函数的代码:
int check(double x)
{
int tot = 0;
int pos = 0;
for(int i = 0;i < N;i++)
{
while(pos < M && (double)A[i]/(double)B[pos] >= x) pos++;
tot += pos;
}
return tot;
}
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 100005;
const double EPS = 0.001;
int A[MAX],B[MAX];
int N,M,K;
int check(double x)
{
int tot = 0;
int pos = 0;
for(int i = 0;i < N;i++)
{
while(pos < M && (double)A[i]/(double)B[pos] >= x) pos++;
tot += pos;
}
return tot;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&N,&M,&K) ;
for(int i = 0;i < N;i++) scanf("%d",&A[i]);
for(int i = 0;i < M;i++) scanf("%d",&B[i]);
sort(A,A + N);sort(B,B + M);
double l = 0,r = A[N-1];
while(r - l > EPS)
{
double mid = (r + l) /2;
if(check(mid) < K) r = mid;//检查大于mid的数有多少个
else l = mid;
}
printf("%.2f\n",l);
}
return 0;
}
/*
2
5 5 3
1 2 3 4 5
2 3 4 5 6
5 5 2
1 2 3 4 5
2 3 4 5 6
*/