题解 AT4867 【[ABC155D] Pairs】
Nemlit
2020-02-17 13:07:24
~~这其实是一篇求助帖~~
要求出第k小的乘积,有负数有正数,不太好处理,于是不难想到二分答案,所以问题转化成给定一个k,求出有多少对数的乘积$\leq mid$
我们分两类类讨论:
$Case1:$ 如果我们二分的值$x>=0$,所有乘积$<=0$的数对全部成立,那么任意负数与正数都成立,任意的0与所有非零数都成立,所有0内部组合也成立
然后我们考虑乘积大于0的。枚举每一个数,那么合法的另一个数的取值一定是在一段区间,二分找到端点就可以了。
$Case2:$ 对于所有$x<0$的二分值,一定是需要一个负数乘以一个正数,也可以用二分来找到对应合法区间
总复杂度$O(N*log_2N*log_2{10^{18}})$,其实二分值域以后内部可以用双指针来实现,复杂度为$O(N*log_2{10^{18}})$。
~~好的回归正题,这是一篇求助帖:~~
为什么$lower$_$bound$不加一些边界处理会有问题啊?(见代码打注释的那两行)
### $Code:$
```
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define int long long
#define D double
il int read() {
re int x = 0, f = 1; re char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
#define rep(i , a , b) for(int i = (a) , i##Limit = (b) ; i <= i##Limit ; ++ i)
#define maxn 200005
int n, m, a[maxn], ans, Mi = -1e18, Ma = 1e18, c1, c2, b[maxn], c[maxn], g, d[maxn], c3;
il int check(int x) {
int tem = 0;
if(x >= 0) {
tem = g * (c1 + c2) + g * (g - 1) / 2 + c1 * c2;
rep(i, 1, c1 - 1) tem += upper_bound(b + i + 1, b + c1 + 1, x / b[i]) - b - 1 - i;
rep(i, 1, c2 - 1) tem += upper_bound(c + i + 1, c + c2 + 1, x / c[i]) - c - 1 - i;
}
else {
x = -x;
rep(i, 1, c1){
int pos = lower_bound(c + 1, c + c2 + 1, (int)ceil((D)x / (D)b[i])) - c;
////对就是这两行
if(b[i] * c[pos] < x) pos++;
if(b[i] * c[pos - 1] >= x) -- pos;
////对就是这两行
if(c2 >= pos) tem += c2 - pos + 1;
}
}
return tem >= m;
}
signed main() {
n = read(), m = read();
rep(i, 1, n) a[i] = read();
rep(i, 1, n) {
if(a[i] < 0) b[++ c1] = -a[i];
else if(a[i] > 0) c[++ c2] = a[i];
else ++ g;
}
sort(b + 1, b + c1 + 1), sort(c + 1, c + c2 + 1);
while(Mi < Ma){
int Mid = (Mi + Ma) >> 1;
if(check(Mid)) Ma = Mid;
else Mi = Mid + 1;
}
cout << Ma;
return 0;
}
```