题意:有n个数,求有几个区间中第k大的数大于等于m
思路:考虑枚举左端点,找一个满足的右端点,求最小满足的那个,就是二分求满足的最左端点。
类似于lower_bound,可以看我以前的博客:http://blog.youkuaiyun.com/thehide/article/details/52078438
以下是二分的代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#define maxn 200100
using namespace std;
int presum[maxn];
int main()
{
int t,a;
scanf("%d",&t);
while(t--){
memset(presum,0,sizeof presum);
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a);
presum[i] = presum[i-1];
if(a >= m) presum[i]++;
}
long long ans = 0;
for(int i = 1;i+k-1 <=n;i++){
int low = i+k-1,high = n;
int mark = -1;
while(low <= high){
int mid = (low+high) >>1;
int cnt = presum[mid] - presum[i-1];
if(cnt >= k){
mark = mid;
high = mid-1;
}
else{
low = mid + 1;
}
}
if(mark!=-1) ans += n - mark + 1;
}
printf("%I64d\n",ans);
}
return 0;
}
官方题解:双指针?还是尺取法..
将不小于m的数看作1,剩下的数看作0,那么只要区间内1的个数不小于k则可行,枚举左端点,右端点可以通过two-pointer求出。
时间复杂度O(n)。
附上自己写的代码..感觉快超时了。#include <stdio.h>
#define maxn 200100
int a[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);a[i] = a[i]>=m?1:0;
}
int low = 0,high = 0,cnt = 0;
long long ans = 0;
while(high < n){
while(high < n && (cnt = cnt + a[high++]) < k);
while(cnt == k){
ans+=(long long)n-high+1;
cnt -= a[low++];
}
}
printf("%I64d\n",ans);
}
return 0;
}