K-th Number HDU - 6231(二分+尺取)
题意 :
给一个长度为 n 的数组 a ,把所有长度大于等于 k 的区间中的第 k 大值插入 b 数组中, 问 b 数组的第 m 大数是多少
思路 :
我们看答案只是一个数,所以考虑二分答案,我们每次二分出这个第M大的值为X,那么我们在原数组中尺取左右端点L,R,如果在[L,R]范围内大于这个X的数出现大于等于K个那么就意味着这右端点之后的每个区间它的第K大一定也会是大于X的(这里是重点),知道了这一点左右尺取范围内大于X的数等于K的区间,每次通过右边界位置就能找出所有第K大大于X的区间的个数,并且这个个数是根据X的变大而变小的所以有了单调关系,那么这个二分就是成立的。
记得要开long long 否则会wa掉 /(ㄒoㄒ)/~~
代码 :
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
#define inf 0x3f3f3f3f
const int maxn = 1e5 + 10 ;
ll n , k , m , a[maxn] ;
bool check(ll x){
ll j = 0 , cnt = 0 , res = 0 ;
for(ll i = 1 ; i <= n ; i++){
while(j < n && cnt < k) if(a[++j] >= x) cnt++ ;
if(cnt == k) res += n - j + 1 ;
if(a[i] >= x) cnt-- ; //区间改变时要记得减去不符合题意的一个区间
}
return res >= m ;
}
int main(){
ios::sync_with_stdio(false) ;
cin.tie(0) ;
cout.tie(0) ;
int t ;
cin >> t ;
while(t--){
cin >> n >> k >> m ;
for(ll i = 1 ; i <= n ; i++) cin >> a[i] ;
ll l = 1 , r = inf , ans = 0 ;
while(l <= r){
ll mid = (l + r) >> 1 ;
if(check(mid)) ans = mid , l = mid + 1 ;
else r = mid - 1 ;
}
cout << ans << endl ;
}
return 0 ;
}