版权声明: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;
}