对于一个长度为n的序列和一个长度为m的序列,它们之间任意两个数的乘积构成了一个长度为n*m的新序列,n和m的范围为10^5,这m*n的范围是10^10,
问新序列中第k大的数是少,
对于这样一个问题,由于范围太大且数组开不下,所以不能采取常规的排序或者树状数组来做,那么剩下想到的只有二分了。
首先将长度为n的a序列和长度为m的b序列分别排序,如果从小到大排序,那么新序列的范围就是a[0]*b[0]~~a[n-1]*b[m-1],然后在这个范围内二分每次
找寻中间值mid,每找到一次就枚举a数列依次然后二分b数列,找到乘积不大于mid的个数然后与k比较,若小于k,则说明mid小了,继续往右边二分不然就是
mid大了然后往左边二分。就是二分次数,讲的不清楚具体看代码吧。
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 using namespace std; 5 const int M=1e5+10; 6 typedef long long ll; 7 ll a[M],b[M]; 8 9 int main() { 10 int n,m,i;ll k; 11 while(~scanf("%d%d%I64d",&n,&m,&k)){ 12 for(i=0;i<n;i++) scanf("%I64d",&a[i]); 13 for(i=0;i<m;i++) scanf("%I64d",&b[i]); 14 sort(a,a+n); 15 sort(b,b+m); 16 k=(ll)n*m-k+1; 17 ll maxMul=a[n-1]*b[m-1]; 18 ll l=a[0]*b[0],r=maxMul,mid,cnt,answer = 0; 19 while (l <= r) { 20 mid = l + r >> 1; 21 cnt = 0; 22 for(int i = 0; i < n; ++i) { 23 int sl = 0, sr = m - 1, smid, scnt = 0; 24 for(; sl <= sr;) { 25 smid = sl + sr >> 1; 26 if(a[i]*b[smid] > mid) sr = smid - 1; 27 else scnt = smid + 1, sl = smid + 1; 28 } 29 cnt += scnt; 30 } 31 if(cnt < k) { 32 l = mid + 1; 33 } else { 34 r = mid - 1; 35 answer = mid; 36 } 37 } 38 printf("%I64d\n", answer); 39 } 40 return 0; 41 }