题目地址:http://www.joyoi.cn/problem/tyvj-1305
单调(队列/栈)是一种有着DP思想的数据结构,它维护一个满足特殊单调性的序列,这种单调性保证每个元素在全局上都有成为最优答案的可能,而不满足这种单调性的元素在之后的统计中一定不会对答案产生任何贡献,需要及时删除。
对于这题,我们首先把寻找子段转化为前缀和之差最大。然后固定住右端点,不断枚举左端点,使区间内的元素不超过M个。
考虑在i固定的情况下,对于任意的j和k两个下标,若有 k<j且sk>=sj k < j 且 s k >= s j ,那么k这个元素是一定不如j的,因为我们的目标是 si−sk s i − s k 最大,并且j比k还要靠近i,j也可以更新i后面的元素,这样我们就找到了符合题意的单调性。
因此我们维护一个下标递增且前缀和递增的队列,每一次入队一个i,就删除在i前面所有不满足“单调性”的元素,即当i入队后,这些元素一定不如i,在i的存在下这些元素一定不会被选入最优答案集合中,也就是类似
sj<=si且j<i
s
j
<=
s
i
且
j
<
i
**的元素
这样队列就具有前缀和递增的单调性,删除的时候直接让队尾出队即可。
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 300000 + 10;
int n,m,ans,q[MAXN],a[MAXN],l=1,r=1,s[MAXN];
int main() {
scanf("%d %d", &n, &m);
for(int i=1; i<=n; i++) {
scanf("%d", a + i);
s[i] = a[i] + s[i-1];
}
for(int i=1; i<=n; i++) { // 两头都可动的区间,最好固定右端点,问题转化为枚举左端点
while(l <= r && q[l] < i - m) l++; // q保存下标 si - sj 是j+1到i的和,实际是 i - (l+1) + 1 > m
ans = max(ans, s[i] - s[q[l]]);
while(l <= r && s[q[r]] >= s[i]) r--; //核心,及时弹出对答案贡献一定不如i的元素
q[++r] = i;
}
printf("%d", ans);
return 0;
}