本题的模型是典型的求第k小问题,这个问题有2个不一样的点,一是任意选出2个数,不能是同一个,二是这个题有负数,那我们在原有的基础上就需要特判这两点,经典模型是2个数组相乘,此处是1个,那么一样可以枚举每一个数,计算比该数小的数的数量,运用容斥,将重复的去掉即可,第一个问题就解决了,假设要判断的数是a,当前枚举到的数是b,第二个问题就是有0有负数有正数,b=0的情况很简单,若a大于0,那么一定所有的数乘a都小于a,直接累加即可,b=正数的情况就比较经典,二分查找比a/b小的数,累加即可,b=负数呢,那就反过来找呗,找比a/b大的数,用二分查找大于等于a/b的数,N-数量就是累加量,因为a是负的,负的乘比他小的负的一定变大,则b*比a/b大的数一定小于a,本题还要注意一个点,在a/b时,若是异号相除,取整时会出问题,需要特判各种情况,当b>0时,我们找的是正向的<=区间,直接对a/b向下取整就可以了,当b<0时,我们找的是反向的>区间,我们也需要"向下取整",但这里的b是负数,我们直接用floor取整会导致统计到的数变多,举个例子,7/3取整为2,2*3<7,floor(7/-3)=-3,-3*(-3)>7,那我们就要反过来向上取整,用ceil,这样保证取整后的数*b<a,总之就是取整后的数一定要保证*b<a,如果有疑问可以自己举几个例子,7/-3,6/-3.-6/4,-6/3等
#include<bits/stdc++.h> using namespace std; #define lowbit(x) ((x)&(-x)) typedef long long LL; const int maxm = 2e5+5; LL n, k; LL buf[maxm]; bool check(LL x) { LL cnt = 0, i, j; for(i = 0; i < n; ++i) { if(buf[i]*buf[i] <= x) cnt--; if(buf[i] > 0) { j = floor((long double)x / buf[i]); LL pos = upper_bound(buf, buf+n, j) - buf; cnt += pos; } else if(buf[i] < 0) { j = ceil((long double)x / buf[i]); LL pos = lower_bound(buf, buf+n, j) - buf; cnt += n - pos; } else { if(x >= 0) cnt += n; } } cnt /= 2; return cnt >= k; } void run_case() { cin >> n >> k; for(int i = 0; i < n; ++i) cin >> buf[i]; sort(buf, buf+n); LL l = -(1LL<<60), r = 1LL<<60, ans, mid; while(l <= r) { mid = (l+r)>>1; if(check(mid)) { ans = mid; r = mid-1; } else l = mid+1; } cout << ans; } int main() { ios::sync_with_stdio(false), cin.tie(0); //cout.setf(ios_base::showpoint);cout.precision(8); run_case(); cout.flush(); return 0; }