题目描述
给定一个长度为几的序列 a1,42,…, an,请问多少种方案,能够从中选取一个长度恰好为 m 的子段,且子段内
所有数字的最大值不超过 K?
输入格式
输入共两行:
输入第一行,
三个正整数n,m,K
输入第二行,几个整数 a1,a2,…, an 。
输出格式
输出一个整数,表示方案数。
数据范围
。对于 30% 的数据,1< m <n< 10
。 对于 60% 的数据,1< m <n< 103
。对于 100% 的数据,1≤ m≤n≤ 105 且-10° ≤ ai,K≤ 109
样例数据
输入:
525
37251
输出:
2
说明:
选取{2,5}和{5,1}均可,
共两种方案
分析
知识点:双指针
解题思路:
考虑固定左端点 l l l 的时候 r = l + m − 1 r=l+m-1 r=l+m−1 而当左端点右移 1 位的时候, r r r 也只会改变 1
考虑对于在 r + 1 r+1 r+1 新加入的数 x x x 如果它大于 k k k ,那么所有左端点在 [ l + 1 , r + 1 ] [l+1,r+1] [l+1,r+1] 内的数都不可能成为答案
也就是说,如果我们在确定右端点的时候在 i ≤ r i\leq r i≤r扫到一个大于 k k k 的数,此时我们把 左端点移到 i + 1 i+1 i+1 再重新确定右端点即可
容易发现,这么做的时候,左端点和右端点扫过的区域都是不降的
复杂度 O ( n ) O(n) O(n)
代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define LL long long
using namespace std;
int const N=1e6+10,mo=1e9+7;
int n,m,l,r,ans,k,a[N];
LL T,s;
void Get(){
for(int i=l;i<=l+m-1;i++) if(a[i]>k){
l=i+1; return;
}
r=l+m-1;
}
int main(){
freopen("e.in","r",stdin);
freopen("e.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
l=1,r=0;
while(r<=n){
while(!r&&l+m-1<=n) Get();
if(!r) break;
ans++;
// cout<<l<<' '<<r<<endl;
l++; r++;
if(a[r]>k) l=r+1,r=0;
}
printf("%d",ans);
}