4875: 第k大数
时间限制: 10 Sec 内存限制: 128 MB
提交: 63 解决: 21
题目描述
有两个序列a,b,它们的长度分别为n和m,那么将两个序列中的元素对应相乘后得到的n*m个元素从大到小排列后的第k个元素是什么?
输入
输入的第一行为一个正整数T (T<=10),代表一共有T组测试数据。
每组测试数据的第一行有三个正整数n,m和k(1<=n, m<=100000,1<=k<=n*m),分别代表a序列的长度,b序列的长度,以及所求元素的下标。第二行为n个正整数代表序列a。第三行为m个正整数代表序列b。序列中所有元素的大小满足[1,100000]。
输出
对于每组测试数据,输出一行包含一个整数代表第k大的元素是多少。
样例输入
33 2 31 2 31 22 2 11 11 12 2 41 11 1
样例输出
311
【分析】:直接求解很明显数据量太大。
二分枚举。
相乘后的新序列的最大值和最小值很容易得出,然后对这个区间进行二分枚举。
有点像猜数。
对每次猜的数x,求一下有多少个数比x大,直到猜的数恰好有k个数比x大,计算结束。
如何求比x大的数有多少个:
序列a,b都按从大到小排序
设两个下标i=0,j=m-1分别跑a和b;
看代码中的函数 f( ll x ) 就能看懂。
【代码】:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,T;
ll a[101010],b[101010];
bool cmp(ll c,ll d)
{
return c>d;
}
ll f(ll x)//统计>=x的数量
{
ll c=0,j=m-1;
for(int i=0;i<n;i++)
{
while(j&&a[i]*b[j]<x)j--;
if(a[i]*b[j]>=x)c+=j+1;
}
return c;
}
int main()
{
cin>>T;
while(T--)
{
cin>>n>>m>>k;
for(int i=0;i<n;i++) scanf("%lld",&a[i]);
for(int i=0;i<m;i++) scanf("%lld",&b[i]);
sort(a,a+n,cmp);
sort(b,b+m,cmp);
ll mid,r=a[0]*b[0],l=a[n-1]*b[m-1];
while(l<r)
{
mid=(l+r)/2;
if(f(mid)>=k)
l=mid+1;
else r=mid;
}
if(f(l)<k)l--;//测试发现有时少1,干脆特判了一下...
cout<<l<<endl;
}
}
/**************************************************************
Problem: 4875
User: summer17083
Language: C++
Result: 正确
Time:1192 ms
Memory:3280 kb
****************************************************************/