题目链接:https://atcoder.jp/contests/abc155/tasks/abc155_d
题意:给你n个数,问两两相乘的第k大的数是多少。
思路:这一题是个二分的好题。首先考虑二分答案,那我们check函数只需要判断找出这个mid是两两相乘第几大的数。那该如何判断呢?我们可以考虑二分判断,开始我们将所有数按大于等于0和小于0进行分类并排序,之后我们每次在大于等于0,小于0以及大于等于0和小于0之间每次二分查找有多少对乘积比mid小就好啦。这样复杂度就是O( n l o g 2 n n{log}^{2}n nlog2n)具体详见代码。
AC代码:
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 2e5 + 7;
const LL INF = 1e18;
vector<LL>v1,v2;
LL k;
bool check(LL x) {
int len1 = v1.size() , len2 = v2.size();
LL ans = 0;
/// < * <
for(int i = 0 ; i < len1 ; i++) {
LL l = i + 1 , r = len1 - 1;
LL res = len1;
while(l <= r) {
LL mid = (l + r + 1LL) >> 1LL;
if(v1[i] * v1[mid] <= x) {
res = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
ans += len1 - res;
}
/// > * >
for(int i = 0 ; i < len2 ; i++) {
LL l = i + 1 , r = len2 - 1;
LL res = i;
while(l <= r) {
LL mid = (l + r + 1LL) >> 1LL;
if(v2[i] * v2[mid] <= x) {
res = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
ans += res - i;
}
/// < * >
for(int i = 0 ; i < len1 ; i++) {
LL l = 0 , r = len2 - 1;
LL res = len2;
while(l <= r) {
LL mid = (l + r + 1LL) >> 1LL;
if(v1[i] * v2[mid] <= x) {
res = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
ans += len2 - res;
}
return ans >= k;
}
int main() {
int n;
while(~scanf("%d%lld",&n,&k)) {
v1.clear(),v2.clear();
for(int i = 1 ; i <= n ; i++) {
LL x;
scanf("%lld",&x);
if(x < 0) v1.push_back(x);
else v2.push_back(x);
}
sort(v1.begin(),v1.end());
sort(v2.begin(),v2.end());
LL l = -INF , r = INF;
LL ans = 0;
while(l <= r) {
LL mid = (l + r + 1LL) >> 1LL;
if(check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%lld\n",ans);
}
return 0;
}