题解 - 选取子段

题目描述

给定一个长度为几的序列 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+m1 而当左端点右移 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 ir扫到一个大于 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值