K-th Number HDU - 6231 (二分+尺取)

K-th Number

 题目链接:HDU - 6231 

题意:有一个数列a,在a的所有长度不小于k的连续子序列中,把其中的第k大数加入数列b,求数列b的第m大数;

思路:所求数一定是数列a中的数,那么就在a 中任选一个数x,若a的所有长度不小于k的连续子序列中第k大数不小于x的子序列一共有Q个,那么x在数列b中至少是第Q大数;若Q<m,说明x可能找小了;若Q>m,说明x找大了,这里就会发现可以二分查找x;

那么怎么判断x是第几大数呢?这里就用到了尺取法;

用尺取法来找数列a中有几个区间的第k大数大于等于x;

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
long long n, m, k;
long long a[maxn], b[maxn];
bool check(long long j, long long cnt, long long x){
	if(a[j]>=x) cnt++;
	return cnt<k;
}
bool cal(long long x){
	long long cnt=0, num=0;
	if(a[0]>=x) cnt++;
	for(long long i=0, j=0; i<n; i++){
		while(j+1<n&&check(j+1, cnt, x)){
			j++;
			if(a[j]>=x) cnt++;
		}
		num+=(n-j-1);
		if(a[i]>=x) cnt--;
	}
	return num>=m;
}
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d%d%d", &n, &k, &m);
		for(long long i=0; i<n; i++){
			scanf("%d", &a[i]);
			b[i]=a[i];
		}
		sort(b, b+n);
		long long l=0, r=n-1;
		long long ans=0;
		while(l<r){
			int mid=(l+r)>>1;
			if(cal(b[mid])){
				l=mid+1;
				ans=mid;
			}
			else{
				r=mid-1;
			}
		}
		printf("%lld\n", b[ans]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sirius_han/article/details/81138895