题意
- 给定一个长为n的数组a,对于他的每个区间拿出他的第K大元素,将所有的第K大元素进行排序,请问其中的第M
大是多少
思路
- 转化1:第K大数比X小的区间个数不能超过M-1个
- 转化2:区间中大于等于X的元素超过K个的区间小于M个
- 转化3:二分M,按上述要求检查所有可能的区间
- 转化4:对于找区间这个事情,如果找到了一个说明这个区间右界往后的每一个区间超过X的数都多余K个——双指针
注意
- 第K大是一个神奇的概念,例如{1,1,1,2,2,2,2,2,3},第3,4,5 大数都是2,所以当x=2,k=4时,不能只考虑大于的,等于的也得考虑,同理,在后面更新左界的时候,就不能取等
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,m,a[202020];
int judge(int x){
int l=1,r=1;
int cntm=0;
int cntk=0;
for(;r<=n;r++){
if(a[r]>=x)cntk++;//这里可以取等,恶心
if(cntk==k){
while(a[l]<x){
cntm+=n-r+1;
l++;
}
cntm+=n-r+1;
l++;
cntk--;
}
}
if(cntm>=m)return 1;
else return 0;
}
signed main(){
int t;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&k,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
int l=1,r=1e9+10;
//二分第m大数x
while(l<=r){
int x=(l+r)>>1;
if(judge(x))l=x+1;
else r=x-1;
}
printf("%lld\n",r);
}
return 0;
}