51Nod1105

版权声明:Power by PowderHan https://blog.csdn.net/weixin_42827051/article/details/81287662

51Nod 1105 第K大数

原题传送门


题目大意
给定长度为 n 的数组 A 和 B,将数组 A 和 B 数组中的元素两两相乘,等到长 度为 n∗n 的数组 C,求 C 中第 K 大数。
数据范围 n≤ 50000,ai,bi ≤ 109。


思路
考虑计算对于 m,C 中有多少数 ≤m,记这个数量为 dm。
将 A 和 B 分别排序,计算对于 Ai,有多少 j 满足 Bj ∗Ai ≤m:在排序后的 B 中找到最后一个 ≤ m Ai 的数即可。
对于单个 m,可以 O(nlogn) 计算 C 中 ≤m 的数量。
求第 K 大的数为多少,即求最小的 m 使得,dm ≥K。
二分 m 的取值,再用之前的方法求 dm,复杂度 O(nlognlog1018)。

这里具体实现时 将第k大转换为了第n^2-k+1小 其实是完全一样的
同时需要注意到数据范围 需要使用long long


Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
using namespace std;

char rd;    int pn;
template<typename Type>
inline void read(Type& v)
{
    pn=1;
    while((rd=getchar())<'0'||rd>'9')
        if(rd=='-')
            pn=-1;
    v=rd-'0';
    while((rd=getchar())>='0'&&rd<='9')
        v=v*10+rd-'0';
    v*=pn;
}
template<typename Type>
inline void out(Type v,bool c=1)
{
    if(v==0)
        putchar(48);
    else  
    {
        if(v<0)
        {
            putchar('-');
            v=-v;
        }
        int len=0,dg[20];  
        while(v>0)
        {
            dg[++len]=v%10;
            v/=10;
        }  
        for(int i=len;i>=1;i--)
            putchar(dg[i]+48);  
    }
    if(c)
        putchar('\n');
    else
        putchar(' ');
}

#define LL long long
const int MAXN=50005;
LL a[MAXN],b[MAXN];
LL n,k;

inline bool cmp(int a,int b)
{
    return a>b;
}

void init()
{
    read(n);    read(k);
    for(int i=1;i<=n;i++)
    {
        read(a[i]); 
        read(b[i]);
    }
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    k=(n*n)-k+1;
}

int find_num(LL x)
{
    return upper_bound(b+1,b+n+1,x)-(b+1);
}

bool check(LL x)//比x/a[i]更小的数的个数统计和
{
    LL tot=0;
    for(int i=1;i<=n;i++)
        if(a[i]>x)
            break;
        else
            tot+=find_num(x/a[i]);
    if(tot>=k)
        return 1;
    else
        return 0;
}

void work()//找最大的m,使得dm<=k
{
    LL l=0,r=(1e18)+9;
    LL ans=r;
    LL mid;
    while(l<r)
    {
        mid=l+(r-l)/2;
        if(check(mid))
        {
            ans=min(ans,mid);
            r=mid;
        }
        else
            l=mid+1;
    }
    cout<<ans<<endl;
}

int main()
{
    init();
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42827051/article/details/81287662