题目大意:
给你三个数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; }