题目大意:
给你三个数n,m,k
然后会根据n,m生成数列
1×1,1×2,······,1×m
2×1,2×2,······,2×m
···
n×1,n×2,······,n×m
问你将这些数字中第k大的数字是多少
思路:
两种做法
一种是堆
一种是二分
看到这题的时候想到了这两种做法但又不知道具体咋做 - -
第一种堆思路
设置一个pair类型的堆
每次就插入从1×m到n×m的元素依次插入值与左边下标进入堆中
然后算第k大的时候就每次找堆顶元素
把堆顶元素减去一个下标再放入堆中(这样就代表了n*(m-1)的元素进入了堆中
然后反复操作找第k大即可
时间复杂度是 nlogn+klogn
堆代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue<pair<ll,ll>>q;
int main()
{
ll n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
q.push(make_pair(i*m,i));
}
pair<ll,ll>temp;
while(--k){
temp=q.top();
q.pop();
temp.first-=temp.second;
q.push(temp);
}
cout<<q.top().first;
return 0;
}
第二种思路
二分
把思路转化成求第k小的数字
把答案设为ans
建一个1到n的for循环(下标为i)
如果 ans/i >= m就说明肯定它前面至少是有m个数字的
小于的时候ans/i就是它前面数字的答案了
那么直接check的时候算ans+=min(ans/i,m)
然后二分就好了
二分代码:
#include <iostream>
using namespace std;
typedef long long ll;
ll n,m,k;
int check(ll x){
ll ans=0;
for(int i=1;i<=n;i++){
ans+=min(m,x/i);
}
return ans>=k;
}
int main()
{
cin>>n>>m>>k;
ll l=0,r=1e13;
ll mid;
k=n*m-k+1;//转化为第k小数字
ll ans=0;
while(l<=r){
mid=l+(r-l)/2;
if(check(mid)){
ans=mid,r=mid-1;//ans为最后一次mid符合要求的情况
}else l=mid+1;
}
cout<<ans<<endl;
return 0;
}