在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。现在,
新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。
然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。FJ有N
(1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,
奶牛i的效率为E_i(0 <= E_i <= 1,000,000,000)。
靠近的奶牛们很熟悉,因此,如果FJ安排超过K只连续的奶牛,那么,这些奶牛就会罢工
去开派对:)。因此,现在FJ需要你的帮助,计算FJ可以得到的最大效率,并且该方案中
没有连续的超过K只奶牛。
用F[i]表示最后一个选择第i只奶牛的最大值。状态转移方程和边界是非常显然的:
F[i]=max{F[j]+sum[i]-sum[j+1]},i-k-1<=j<i-1
考虑优化,一眼看到单调队列,把sum[i]提出来,变成
f[i]=max{f[j]-sum[j+1]}+sum[i],i-k-1<=j<i-1
所以在算f[i]的时候,问题转化为求f[j]-sum[j+1]的最大值,这个询问区间是单调递增且定长的。
于是,先将F[ 1 ~ k ]求出(=sum[ i ]),然后把F[1~k-1]-sum[2~k]放到(push)单调队列中。
然后从i=k~n,对于每个i,先用单调队列O(1)的更新,然后push(i-1)注意不是 i !然后弹出i-k-1。
但是这样只能得90分。剩下的一个点是k=1的情况。这个时候,计算F[2]的时候,单调队列中为空,要特判。
丑陋的代码如下:
//codevs P4645
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 100010
#define ll long long
using namespace std;
ll sum[MAXN],f[MAXN],ans;
int n,k,x;
template<typename Tp>
struct mtc_queue{
private:
int q[MAXN],fp,rp,vsz;
Tp v[MAXN];
public:
mtc_queue()
{
fp=1;rp=vsz=0;
memset(v,0,sizeof(v));
}
inline void clear()
{
fp=1;rp=vsz=0;
}
inline bool empty()
{
return (rp+1==fp)?true:false;
}
inline void push(const Tp &val)
{
while(!empty()&&v[q[rp]]<=val) rp--;
v[q[++rp]=++vsz]=val;
}
inline void pop(int pos)
{
if(q[fp]==pos) fp++;
}
inline Tp front()
{
return v[q[fp]];
}
};
mtc_queue<long long> q;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
sum[i]=sum[i-1]+x;
}
//f[i]=max{f[j]+sum[i]-sum[j+1]},i-k-1<=j<i-1
for(int i=1;i<=k;i++)
{
f[i]=sum[i];
if(i!=k) q.push(f[i]-sum[i+1]);
}
for(int i=k+1;i<=n;i++)
{
// for(int j=i-k-1;j<i-1;j++) f[i]=max(f[i],f[j]+sum[i]-sum[j+1]);
if(!q.empty()) f[i]=q.front()+sum[i];
else f[i]=sum[i]-sum[i-1];
if(!q.empty()) q.pop(i-k-1);
q.push(f[i-1]-sum[i]);
ans=max(ans,f[i]);
}
printf("%lld\n",ans);return 0;
}