1. 程式人生 > >bzoj4385 Wilcze doły 單調佇列

bzoj4385 Wilcze doły 單調佇列

       (趕腳POI要被許可權。。趕緊趁現在刷幾道題)。

       首先令s[i]表示(1,i)序列的和。現在考慮一段序列(i,j),如何判斷這段序列是否滿足條件呢?當j-i+1<=d時顯然滿足;否則,我們肯定是要讓這一段序列中總和最大的一段(x,x+d)變為0,換句話說,如果設x,使x滿足i<=x,x+d<=j且s[x+d]-s[x]值最大,那麼如果s[i]-s[j-1]-(s[x+d]-s[x])<=p,那麼(i,j)滿足條件。

       因此我們列舉右端點j,顯然i隨著j的增大而增大,x也隨之增大。因此我們用單調佇列維護此時(i,j)的x,然後如果s[i]-s[j-1]-(s[x+d]-s[x])>p就不斷i++,第一個滿足條件的i就是當右端點為j時左端點的最大延伸長度。

       時間複雜度O(N)。

AC程式碼如下(實現與上述描述有所不同):

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

int n,d,q[2000005]; ll s[2000005],p;
ll read(){
	ll x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int main(){
	scanf("%d%lld%d",&n,&p,&d); int i;
	for (i=1; i<=n; i++) s[i]=s[i-1]+read();
	int ans=d,head=1,tail=0,j=0;
	for (i=d; i<=n; i++){
		while (head<=tail && s[i]-s[i-d]>s[q[tail]]-s[q[tail]-d]) tail--;
		q[++tail]=i;
		while (s[i]-s[j]-s[q[head]]+s[q[head]-d]>p){
			j++; if (q[head]-d<j) head++;
		}
		ans=max(ans,i-j);
	}
	printf("%d\n",ans);
	return 0;
}

by lych

2016.3.3