P1714,用单调队列切最大的蛋糕

· · 题解

这是一道考察单调队列优化dp的好题。

首先我们先来看题目:

小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大

我们可以考虑用一维前缀和来进行预处理 即:

    for(fint i=1;i<=n;i++)
        a[i]=read();
    for(fint i=1;i<=n;i++)
        s[i]=s[i-1]+a[i];

接下来,我们接着往下看,

但小Z最多又只能吃M小块(M≤N)的蛋糕。

这可怎么办呢,难道需要暴力枚举每一个搭配吗?再看数据范围:

对20%的数据,N≤100。

对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。

数据范围在int_max附近,所以我们想ac显然需要别的方法。

这时候,我们今天的主角----单调队列就要隆重登场了!

那么神马是单调队列呢?

我们可以把单调队列想象成一个长度为m的滑动窗口,在不停的滑动中所包含的值也不一样,而单调队列同时又满足单调性,即单调递增或单调递减(数学必修一)。

所以我们可以用两头均可出入的特殊数据结构————双向队列,来帮助我们实现。

code

    head[0]=head[1]=1;
    for(fint i=1;i<=n;i++)
    {
        while(!q.empty()&&q.back().cnt>=a[i].cnt)
        q.pop_back();
        q.push_back(a[i]);
        while(!s.empty()&&s.back().cnt<=a[i].cnt)
        s.pop_back();
        s.push_back(a[i]);
        if(i-head[0]>=k)
        {
        head[0]++;
        if(i-k==q.front().num)
        q.pop_front();
        }
        if(i-head[1]>=k)
        {
        head[1]++;
        if(i-k==s.front().num)
        s.pop_front();
        }
        if(i>=k)
        {
        v[i]=q.front().cnt;
        w[i]=s.front().cnt;
        }
    }

s中储存的即为减队列,q中储存的即为加队列。

类比到这道题,我们一样可以用单调队列来优化:

    int head=1;
    int maxxs=-inf;
    for(fint i=1;i<=n;i++)
    {
        while(!q.empty()&&s[q.back()]>=s[i])
        q.pop_back();
        q.push_back(i);
        if(q.front()<i-k)
            q.pop_front();
        maxxs=max(maxxs,s[i]-s[q.front()]);
    }

这便是使用单调队列和前缀和处理后的“蛋糕”。

最后,推荐几道相关题目:P1886单调队列模板题P2627修剪草坪

就到这里了,谢谢大家!