题目传送门
题意
给你n个数,求出最大连续子段和,并且该子段长度不超过m,且不能为空子段。
样例输入
6 4
1 -3 5 1 -2 3
样例输出
7
数据范围
1≤n,m≤300000
思路
对于连续的子段和,我们应该会想到是右端点的前缀和减去左端的前缀和,但是如果枚举左右端点,时间复杂度不现实。
偷偷看了眼标签,单调队列,所以去现学了下单调队列。
单调队列可以用双向队列实现,不会双向队列基本操作的可以去看看别的博主的文章。
对于两个下标j,k,如果k>j且sum[k]<sum[j],那么我们可以知道,k一定优于j,因为对于右端点I,sum[i]-sum[k]>sum[i]-sum[j],并且 i 到 k 的距离要小于 i 到 j 的距离,所以k一定优胜与j。
所以我们可以发现,我们从1到n的枚举前缀和,如果发现了后到的下标的前缀和要小于先前队列尾部的前缀和,那么就可以把它挤掉。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const int INF=0x7fffffff;
const ll LLINF=0x7fffffffffffffff;
const double EPS=1e-10;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define int long long
int a[N],sum[N];
deque<int>q;
signed main()
{
IOS;
//freopen("","r",stdin);
//freopen("","w",stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
int res=-INF;
q.push_back(0);
for(int i=1;i<=n;i++)
{
while(!q.empty()&&q.front()<i-m)
q.pop_front();
res=max(res,sum[i]-sum[q.front()]);
while(!q.empty()&&sum[q.back()]>=sum[i])
q.pop_back();
q.push_back(i);
}
cout<<res<<endl;
}