洛谷P3594 [POI2015]WIL-Wilcze doły

题面

传送门

分析

一道较为简单的单调队列题目

思路和上一道题很像,就是枚举右端点,然后找到满足条件的“最左”的左端点

怎么找呢,我们发现随着右端点的右移,其实左端点也在不断地右移,那么就是左端点单调不减

然后我们就可以想到单调队列来维护了

队列当中维护的信息是什么呢...

左端点位置?不是。

因为这样维护的话我们每次还要在当前区间当中找到最大的长度为\(d\)的区间,很麻烦

所以我们直接维护这个长度为\(d\)的区间的和的最大值,以及这个区间的左端点即可(就是说队列里存的是这个区间和的最大值对应区间的位置,而且在这个过程中,我们顺带记一下左端点\(st\)的位置)

然后总复杂度就是\(O(n)\)的,具体细节看代码

代码

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
const int N=2e6+5;
ll n,p,d,hh,tt=-1,que[N],a[N],pre[N],val[N],st=1,ans; 
int main(){
	read(n),read(p),read(d);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i];
	for(int i=d;i<=n;i++) val[i]=pre[i]-pre[i-d];
	que[++tt]=d,ans=d;
	for(int i=d+1;i<=n;i++){
		while(hh<=tt&&val[i]>val[que[tt]]) tt--;//维护最大的长度为$d$的区间
		que[++tt]=i;
		while(hh<=tt&&pre[i]-pre[st-1]-val[que[hh]]>p) st++;
                while(hh<=tt&&que[hh]-d+1<st) hh++;//如果本身这个最大值已经不在当前$st-i$的区间内,直接弹出
		ans=max(ans,i-st+1);
	}
	write(ans);
	return 0;
}
posted @ 2020-12-12 17:45  __Anchor  阅读(87)  评论(0编辑  收藏  举报