ACM_求第k大元素(两次二分)
求第k大
Time Limit: 6000/3000ms (Java/Others)
Problem Description:
给定两个数组A和B,大小为N,M,每次从两个数组各取一个数相乘放入数组C,最终得到一个N*M的数组C。求C中第K大的数。
Input:
输入包含多组测试数据,每组数据首先输入两个整数N,M,K,接下来一行有N个整数Ai,再接下来一行有M个整数Bi。(1≤N,M≤10^5,1≤Ai,Bi≤10^5,1≤K≤N*M)
Output:
对于每组数据,输出答案。
Sample Input:
3 2 2 1 2 3 1 2 2 2 1 1 1 1 1
Sample Output:
4 1
解题思路:由于数据非常大,因此考虑二分来做:先对两个数组降序排,然后外层二分查找第k大元素,内层二分查找当前mid在数组C中排第几并作为外层二分查找的条件,最终的答案就是l-1,详解看代码。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int maxn=1e5+5; 5 int n,m;LL k,l,r,mid,ans,A[maxn],B[maxn]; 6 inline LL read(){ 7 LL x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 10 return x*f; 11 } 12 inline void print(LL x){ 13 if(x<0)putchar('-'),x=-x; 14 if(x>9)print(x/10); 15 putchar(x%10+'0'); 16 } 17 bool check(LL mid){ 18 LL cnt=0; 19 for(int i=0;i<n;++i){ 20 int lt=0,rt=m-1; 21 while(lt<=rt){///二分找b数组中某个元素与当前A[i]乘积不小于mid的最大下标lt-1 22 int midt=(lt+rt)>>1; 23 if(A[i]*B[midt]>=mid)lt=midt+1;///说明可能还有更小的元素与A[i]相乘之后不小于mid,则往右边找 24 else rt=midt-1;///否则往左边找 25 } 26 cnt+=lt;///退出条件是rt=lt-1,而下标是0开始的,所以lt不用减1,累加当前满足条件的个数 27 } 28 return cnt>=k; 29 } 30 int main(){///时间复杂度为log2(nm)*n*log2(m) 31 while(~scanf("%d%d%lld",&n,&m,&k)){ 32 for(int i=0;i<n;++i)A[i]=read(); 33 for(int i=0;i<m;++i)B[i]=read(); 34 sort(A,A+n,greater<LL>());///降序排 35 sort(B,B+m,greater<LL>()); 36 l=A[n-1]*B[m-1],r=A[0]*B[0]; 37 while(l<=r){///二分找第k大元素(即两个数的乘积) 38 mid=(l+r)>>1; 39 if(check(mid))l=mid+1;///说明mid可以更大,继续往右边找 40 else r=mid-1;///否则往左边找 41 } 42 print(l-1);puts(""); 43 } 44 return 0; 45 }