第k大数
题目描述
有两个序列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大的元素是多少。
样例输入 Copy
3 3 2 3 1 2 3 1 2 2 2 1 1 1 1 1 2 2 4 1 1 1 1
样例输出 Copy
3 1 1
这个题目我wa了三次,因为我偏执地相信是可以直接序列相乘得到新的序列,然后在sort,结果错了这么多次,后来上网看了看题解,发现只有线性时间查找才不会超时
//超时版本:
#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b){
return a>b;
}
int main(){
int n;
int t=0;
while(cin>>t){
while(t--){
int n,m,k;
cin>>n>>m>>k;
int a[n];
int b[m];
int s=n*m;
int c[s+5];
int i,j;
int ans=0;
for(i=0;i<n;i++){
cin>>a[i];
}
for(i=0;i<m;i++){
cin>>b[i];
}
for(i=0;i<n;i++){
for(j=0;j<m;j++){
c[ans++]=a[i]*b[j];
}
}
sort(c,c+s,cmp);
cout<<c[k-1]<<endl;
}
}
}
//AC版本:
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
lint n,m,k;
lint a[100005],b[100005];
bool cmp(lint a,lint b){
return a>b;
}
lint fun(lint s){
lint num=0;
lint i=0;
lint j=n-1;
for(;i<n&&j>=0;i++){
if(a[i]*b[j]>=s)
num=num+j+1;
else{
while(j>=0&&a[i]*b[j]<s)
j--;
if(j>=0)
num=num+j+1;
}
}
return num;
}
lint aa(){
lint left=a[n-1]*b[n-1];
lint right=a[0]*b[0];
lint res=0;
while(left<=right){
lint mid=(left+right)/2;
if(fun(mid)>=k){
left=mid+1;
res=mid;
}
else
right=mid-1;
}
return res;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>m>>k;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(lint i=0;i<n;i++)
cin>>a[i];
for(lint i=0;i<m;i++)
cin>>b[i];
sort(a,a+n,cmp);
sort(b,b+m,cmp);
lint s=aa();
cout<<s<<endl;
}
return 0;
}