题目链接:​​K-th Number​

题目大意:给你一个长度为n的数字,然后需要你找出这个序列里面长度大于等于k的所有子序列里第k小的数,然后扔到另一个数组里面去,然后寻找另一个数组里面第m大的数,问这个数

题目思路:我们可以将问题转化一下:我们需要去二分一个最大的x,且这个x满足有大于等于m个区间的第k小大于等于x(这样的话就满足了这个枚举的数要比我们最后要得到的数要大)。所以关键在于,如何求有多少个区间的第k小大于等于x。一个区间第k小要大于等于x,则这个区间至少要有k个数大于等于x我们枚举区间的左端点L。对于每个左端L,可以找一个最小的r使得,当右端点大于等于r时,[L,r]有k个数大于等于x。所以L为左端点的区间中满足要求的区间数有 n-r+1个,而这个r关于l显然是有单调性的,所以可以考虑用双指针O(n)求出所有r。

时间复杂度&&空间复杂度:O(nlogn)&&O(n)

#include <map>
#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const ll maxn = 1e6+10;

ll a[maxn],b[maxn];
ll T,n,k,m;

bool check(ll mid){
ll ans = 0,num = 0;
ll l = 0,r = -1;
while(r < n){
if(num < k){
if(a[r+1] >= mid) num++;
r++;
}else{
if(num == k) ans += (n-r);
if(a[l] >= mid) num--;
l++;
if(ans >= m) return true;//超过m个区间的第k小大于等于mid
}
}
return false;
}

int main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&n,&k,&m);
for(ll i = 0;i < n;i++){
scanf("%lld",&a[i]);
b[i] = a[i];
}
sort(b,b+n);
ll l = 0,r = n-1,mid,ans;
while(l <= r){
mid = (l+r)>>1;
if(check(b[mid])) ans = b[mid],l = mid+1;
else r = mid-1;
}
printf("%lld\n",ans);
}
return 0;
}