1. 程式人生 > 實用技巧 >洛谷P3594 [POI2015]WIL-Wilcze doły

洛谷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;
}