NanoApe Loves Sequence Ⅱ
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/131072 K (Java/Others)
Total Submission(s): 797 Accepted Submission(s): 376
Total Submission(s): 797 Accepted Submission(s): 376
Problem Description
NanoApe, the Retired Dog, has returned back to prepare for for the National Higher Education Entrance Examination!
In math class, NanoApe picked up sequences once again. He wrote down a sequence with n numbers and a number m on the paper.
Now he wants to know the number of continous subsequences of the sequence in such a manner that the k -th largest number in the subsequence is no less than m .
Note : The length of the subsequence must be no less than k .
In math class, NanoApe picked up sequences once again. He wrote down a sequence with n numbers and a number m on the paper.
Now he wants to know the number of continous subsequences of the sequence in such a manner that the k -th largest number in the subsequence is no less than m .
Note : The length of the subsequence must be no less than k .
Input
The first line of the input contains an integer
T
, denoting the number of test cases.
In each test case, the first line of the input contains three integers n,m,k .
The second line of the input contains n integers A1,A2,...,An , denoting the elements of the sequence.
1≤T≤10, 2≤n≤200000, 1≤k≤n/2, 1≤m,Ai≤109
In each test case, the first line of the input contains three integers n,m,k .
The second line of the input contains n integers A1,A2,...,An , denoting the elements of the sequence.
1≤T≤10, 2≤n≤200000, 1≤k≤n/2, 1≤m,Ai≤109
Output
For each test case, print a line with one integer, denoting the answer.
Sample Input
1 7 4 2 4 2 7 7 6 5 1
Sample Output
18
Source
大体题意:
给你n,m,k,并且给你n 个数列,问有多少个区间满足第k大的数不小于m?
思路:
两种思路,滑动窗口或者二分法!
其实两种思路的大体思路是一样的。
枚举区间的起点,这样只需要找到有多少个终点满足即可! 首先这个终点肯定是要大于等于 i + k - 1 的,(必须保证这个区间内有k个数),
关于滑动窗口:
先找到第一个终点满足大于等于k个数并且 符合条件的数等于k,然后起点在移动,起点移动找到第一个符合条件的数小于k个,并且l < r 这样ans += n- (r-1)+1。 依次循环即可! 详细见代码。
关于二分法:
可以先预处理,令sum[r]表示1,2,3,,r之间有多少个数字符合条件(即数字大于等于m)。
同样的去枚举起点i,在i+k-1 到 n 之间进行二分法,找到左边界符合条件的位置l, ans += n-l+1即可!
详细见代码:
滑动窗口版:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 200000 + 10;
typedef long long ll;
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 = 1; i <= n; ++i)scanf("%d",&a[i]);
ll sum = 0ll;
int l = 1,r = 1;
int isok = 0;
while(1){
int sum1=0,sum2 =0;
for (; r <= n && isok < k; ++r){
if (a[r] >= m)++isok;
++sum1;
}
for (; l <= r && isok >= k; ++l){
// printf("%d %d\n",l,r);
sum += n-(r-1)+1;
if (a[l] >= m)--isok;
++sum2;
}
if (!sum1 && !sum2)break;
}
printf("%I64d\n",sum);
}
return 0;
}
二分法版:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 200000 + 10;
typedef long long ll;
int a[maxn];
int sum[maxn];
int n,m,k;
bool judge(int l,int r){
return sum[r]-sum[l-1] >= k;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d %d",&n,&m,&k);
for (int i = 1; i <= n; ++i){
scanf("%d",&a[i]);
if (a[i] >= m)sum[i] = sum[i-1] +1;
else sum[i] = sum[i-1];
}
ll ans = 0ll;
for (int i = 1; i <= n; ++i){
if (i + k-1 > n)break;
if (!judge(i,n))continue;
int l = i+k-1,r = n;
int mid;
while(l < r){
mid = l+r >> 1;
if (judge(i,mid))r=mid;
else l = mid+1;
}
// printf("mid = %d\n",l);
ans += n-l+1;
}
printf("%I64d\n",ans);
}
return 0;
}