【HDOJ 6231】 k-th number 二分答案
链接
http://acm.hdu.edu.cn/showproblem.php?pid=6231
题意
给你一个数列\(a[1...n]\),对于a中的每个长度大于等于k的区间,把其中第k大的数加进数列b中,求数列b中第m大的数。
solution
二分答案的思想由来
如果任取一个数x作为答案,怎么判断它大了还是小了?如何调整?
- 对于任意一个区间,如果x在其中的排名>m,即比x大的数有至少m个,那么x就不能作为这个区间的结果。
 - 如果x满足上述条件,要让x作为区间的结果,需要增大x的值;反之减小x的值。
 - 随着x的值增大,在数列b中排名一定会上升。满足单调性。
 
至此我们可以使用二分答案的做法:
二分答案x的数值大小,即b中排名<=k的( 区间中有至少k个数满足大于等于x 的 区间个数)数的大小。
如果区间个数>=m,表明至少有m个数排在x之前,更新答案,增大x;反之减小x。
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100005;
const double pi = 3.14159265358979;
ll n,k,m;
int t;
int a[N];
int num[N]={0};
bool check(int mid){
	ll cnt=0;  //>=mid 的区间的个数 
	num[0]=0;
	for(int i=1;i<=n;i++) num[i]=num[i-1]+(a[i]>=mid);
	for(int l=1;l<=n;l++){
		int r=lower_bound(num+1,num+n+1,num[l-1]+k)-num;
		cnt+=(n-r+1);
	} 
	return cnt>=m; 
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k>>m;
		int l=1e9,r=0,mid,ans;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			r=max(a[i],r);
			l=min(l,a[i]);
		}
		while(l<=r){
			mid=(l+r)>>1;
			if(check(mid)) ans=mid,l=mid+1;   // >=mid 的数 >= M 个 
			else r=mid-1; 
		}
		printf("%d\n",ans);
	}
	return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号