题目描述
今天是小 Z 的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了 \(n\) 个相同的小块,每小块都有对应的幸运值。
小 Z 作为寿星,自然希望吃到的蛋糕的幸运值总和最大,但小 Z 最多又只能吃 \(m(m\le n)\) 小块的蛋糕。
请你帮他从这 \(n\) 小块中找出连续的 \(k(1 \le k\le m)\) 块蛋糕,使得其上的总幸运值最大。
形式化地,在数列 \(\{p_n\}\) 中,找出一个子段 \([l,r](r-l+1\le m)\),最大化 \(\sum\limits_{i=l}^rp_i\)。
题目分析
欸?这题这么简单?这不就暴力枚举左右端点,前缀和数组 \(O(1)\) 查询区间和,取最大值不就行了?
嗯……是个好思路,够暴力,但是吧……
对于 \(20\%\) 的数据,有 \(1\le n\le100\)。
对于 \(100\%\) 的数据,有 \(1\le n\le5\times 10^5\)
暴力做法好像不太行
考虑优化
从 \(1\) 到 \(n\) 枚举右端点r
,设此时左端点为l
,最后幸运值总和就是sum[r]-sum[l-1]
由于我们要最大化幸运值,因此我们希望sum[l-1]
最小
题目中还有一个限制
小 Z 最多只能吃 \(m(m\le n)\) 小块的蛋糕。
因此 \(r-l+1 \le m\)
所以我们要找到的就是在以r-1为右端点的,长度为m-1的区间中的最小值
这道题至此就被成功的转化为了滑动窗口求最值的问题
可以用单调队列来维护
AC代码
#include<iostream>
#include<deque>
using namespace std;
const int N=500010;
int n,m;
int a[N];
int ans=-2e9;
deque<int> q;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]+=a[i-1];
}
for(int i=0;i<=n;i++)
{
while(!q.empty()&&q.front()<i-m+1)
q.pop_front();
while(!q.empty()&&a[q.back()]>a[i])
q.pop_back();
q.push_back(i);
ans=max(ans,a[i]-a[q.front()]);
}
cout<<ans<<endl;
return 0;
}