luogu3594 WIL 题解

分析

显然,置零的区间长度一定为 \(d\),否则一定不优。

那么答案大于等于 \(d\)

暴力做法,枚举左右端点 \([i,j]\),贪心地减去区间里长度为 \(d\) 的最大的一块,用前缀和搞一下,\(O(n^3)\)

考虑一个双指针做法,固定左端点 \(i\),贪心地让右端点在 \([i+d,n]\) 中增长,维护区间内最大的长度为 \(d\) 的块。如果区间和大于 \(p\) 了,那么看减去最大块之后是否合法,\(O(n^2)\)

考虑优化这个做法。

不难发现对于每个固定 \(i\)\(j\) 的过程,都要维护区间最大块,但是很多是重复的。反过来,固定右端点,维护最靠前的合法左端点,这个过程是个滑动窗口。而要维护的区间最大块,在这个过程中也是一个滑动窗口。

用单调队列维护当前 \([i,j]\) 区间最大块即可,复杂度 \(O(n)\)

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=2e6+5;
int n, p, d, ans, a[N], s[N], q[N], c[N];
signed main() {
	n=read(), p=read(), d=read();
	for(int i=1;i<=n;++i) a[i]=read(), s[i]=s[i-1]+a[i];
	for(int i=d;i<=n;++i) c[i]=s[i]-s[i-d]; 
    // c[i]表示以i结尾的,长度为d的块的和
	int w=s[d];
	int l=1, r=1;
	q[l]=d;
	ans=d;
	for(int i=d+1,j=1;i<=n;++i) {
        // 这里写的是[j,i]
		w+=a[i];
		while(l<=r&&c[q[r]]<=c[i]) --r;
		q[++r]=i;
		while(l<=r&&(w-c[q[l]])>p) {
            // 当前区间和减去最大块也不能满足
			if(q[l]<j+d) ++l;
            // q[l]<j+d时,q[l]才非法
			w-=a[j++];
            // 这个j就不可能作为左端点了
        }
		ans=max(ans,i-j+1);
	}
	printf("%lld\n",ans);
}

luogu3594 WIL 题解
https://yozora0908.github.io/2022/lg3594-solution/
作者
yozora0908
发布于
2022年10月1日
许可协议