AtCoder D - Pairs(二分!再二分!)

题意:
给你 n n 个数字,然后这些数字两两相乘,共 n ( n 1 ) 2 \dfrac{n\cdot (n-1)}{2} 个数。问你第 k k 个数的大小。
思路:
二分答案。
但是这样复杂度为 n 2 l o g ( n ) n^2log(n) 。妥妥超时,怎样把 n 2 n^2 复杂度降下来。
二分判定
我们可以分为两个集合,一个为正的,一个为负的 。
然后将他们排序,在正的集合里遍历二分,负的集合里遍历二分,最后遍历负的集合,对正的集合遍历二分。
这样二分答案 l o g ( n ) log(n) ,判定 n l o g ( n ) nlog(n) ,总共复杂度 n l o g 2 ( n ) nlog^2(n)

const ll N = 2e5 + 10;
const ll mod = 1e9;
const ll Max_n = 1e6 + 10;
using namespace std;
vector<ll> q,p;
bool ok(ll x,ll n,ll k){
    ll ans = 0;
    sort(q.begin(),q.end(),greater<ll>());
    sort(p.begin(),p.end());
    int s1 = q.size(),s2 = p.size();
    for(int i = 0;i < s1;++i){
        int l = i,r = s1-1;
        while(l < r){
            int mid = l + r +1 >>1;
            if(q[i]*q[mid] <= x) l = mid;
            else r = mid - 1;
        }
        if(l > i) ans += l -i ;
    }
    for(int i = 0;i < s2;++i){
        int l = i,r = s2 - 1;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(p[i]*p[mid] <= x) l = mid;
            else r = mid - 1;
        }
        if(l > i) ans += l - i ;
    }
    if(ans >= k) return 1;
    reverse(q.begin(),q.end());
    reverse(p.begin(),p.end());
    for(int i = 0;i < s1;++i){
        int l = -1,r = s2-1;
        while(l < r){
            int mid = l + r + 1>>1;
            if(q[i] * p[mid] <=  x) l = mid;
            else r = mid - 1;
        }
        if(l != -1) ans += l + 1;
    }
    return ans >= k;

    
}
int main(){
    ll n,k;
    scanf("%lld%lld",&n,&k);
    for(int i = 1;i <= n;++i){
        ll a;
        scanf("%lld",&a);
        if(a<0) q.push_back(a);
        else  p.push_back(a);
    }
    ll l = - mod * mod ,r = mod *mod;
    while(l < r){
        ll mid = l + r >>1;
        if(ok(mid,n,k)) r = mid;
        else l = mid + 1;
    }
    cout << r;
}
发布了589 篇原创文章 · 获赞 31 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/104355029