[WOJ1138]最大子序和

题目链接:###

WOJ1138

题目分析:###

是很经典的一道题,乱搞的方法应该有不少,这里介绍O(n)的单调队列做法
首先维护一个前缀和,然后枚举每一个位置,维护一个前缀和单增的单调队列,但队列仅储存下标,将下标在当前位置的范围k以外的元素出队
因为对于每一个位置,ans=sum[i]-min(sum[j])(1<=i<=n,1<=j<=i)
所以用sum[i]减去单调队列队首的元素对应的前缀和值即可


代码:###

#include<bits/stdc++.h>
#define MAXN (300000+5)
using namespace std;
inline int read(){
	int cnt=0,f=1;char c;
	c=getchar();
	while(!isdigit(c)){
		if(c=='-')f=-f;
		c=getchar();
	}
	while(isdigit(c)){
		cnt=cnt*10+c-'0';
		c=getchar();
	}
	return cnt*f;
}
int n,m,a[MAXN],sum[MAXN],q[MAXN],l,r;
int ans=-(1<<30);
int main(){
	n=read();m=read();
	for(register int i=1;i<=n;i++)a[i]=read(),sum[i]=sum[i-1]+a[i];
	for(register int i=1;i<=n;i++){
		while(sum[i]<sum[q[r]]&&l<=r)r--;
		q[++r]=i;
		while(q[l]<i-m&&l<=r)l++;
		if(sum[i]-sum[q[l]]>ans)ans=sum[i]-sum[q[l]];
	}
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/kma093/p/10293170.html