[Usaco2009 Open]干草堆

来自一个弱鸡的随笔单调队列 动态规划

题目链接:http://forioi.com/contest/problem?id=56&pid=9#problem-anchor

g[j]<=s[i]-s[j],即s[j]+g[j]<=s[i],若有 k<j且g[k]+s[k]>=g[j]+s[j]则k永远不可能再转移别人了,因为如果k满足转移条件,那么j也会满足,而j的位置还更加靠后。

因此,我们可以维护一个 g+s值单调递增的单调队列。

每次转移时从单调队列中找到最后一个g[j]+s[j]<=s[i]的位置j,单调队列中j以左的元素从队首出队。

转以后从队尾弹出g[j]+s[j]>=g[i]+s[i]的元素,然后将位置i加入单调队列。

每个元素进出队列各一次,复杂度为O(n)。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,a[maxn],s[maxn],f[maxn],g[maxn],q[maxn],l,r;
int main()
{
    scanf("%d",&n);
    for(int i=n;i;--i)
        scanf("%d",a+i);
    for(int i=1;i<=n;++i)
        s[i]=s[i-1]+a[i];
    for(int i=1;i<=n;++i)
    {
        while(l<r&&s[q[l+1]]+g[q[l+1]]<=s[i])
            ++l;
        f[i]=f[q[l]]+1;
        g[i]=s[i]-s[q[l]];
        while(l<r&&s[q[r]]+g[q[r]]>=s[i]+g[i])
            --r;
        q[++r]=i;
    }
    printf("%d",f[n]); 
    return 0;
}

有木有很清爽啊~

原文地址:https://www.cnblogs.com/backcl/p/13426301.html