【最大连续子段和】单调队列 + 前缀和优化
题目描述
输入一个长度为 n 的整数序列(A1,A2,……,An),从中找出一段连续的长度不超过 M 的子序列,使得这个序列的和最大。
输入格式
第一行两个数 n,m
第二行由空格隔开的 n 个整数,即 A1 ~ An 。
输出格式
输出一个整数,即最大连续子序列之和。
样例数据 1
备注
【数据范围】
30% 的数据:n<=3000,m<=100
100% 的数据 1<=N,M<=1000000,-maxlongint<=ai<=maxlongint
题目分析
看到了要求一个区间的和,可以想到用前缀和来表示。那么以点(i)结尾的最大连续字段和就为$sum[i] - min\{sum[j]\} (1 <= j < i)$
这里又出现了单调队列的形式:维护前缀和的单调递增序列,入队维护单调性,队首即为最小值,最后比较选出最大值。
code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<cmath> using namespace std; const int N = 1e6 + 5; int n, m; int que[N], head, tail; long long ans, val[N]; inline int read(){ int i = 0, f = 1; char ch = getchar(); for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()); if(ch == '-') f = -1, ch = getchar(); for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0'); return i * f; } inline long long readL(){ long long i = 0, f = 1; char ch = getchar(); for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()); if(ch == '-') f = -1, ch = getchar(); for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0'); return i * f; } inline void wr(long long x){ if(x < 0) putchar('-'), x = -x; if(x > 9) wr(x / 10); putchar(x % 10 + '0'); } int main(){ n = read(), m = read(); for(int i = 1; i <= n; i++) val[i] = readL() + val[i - 1]; que[head = tail = 1] = 1, ans = val[1]; for(int i = 2; i <= n; i++){ while(head <= tail && val[que[tail]] > val[i]) tail--; que[++tail] = i; while(que[head] < i - m && head <= tail) head++; ans = max(ans, val[i] - val[que[head]]); } wr(ans); return 0; }