HDU3507 Print Article

http://acm.hdu.edu.cn/showproblem.php?pid=3507

斜率优化\(DP\)

\[dp_i=\min \{dp_j+(s_i-s_j)^2+m)\}\\ 若j优于k(j>k)\\ dp_j+(s_i-s_j)^2+m)<dp_k+(s_i-s_k)^2+m)\\ dp_j+s_j^2-dp_k-s_k^2<2s_i(s_j-s_k)\\ \frac{dp_j+s_j^2-dp_k-s_k^2}{2(s_j-s_k)}<s_i\\ \]

单调队列尾部的\(r\)可以通过画图来判断什么时候应当左移(本题中维护的是上凸壳)

\(C++ Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 500005
#define ll long long
int n,m;
int q[N];
ll c[N],s[N],dp[N];
using namespace std;
#define x(a,b) (2LL*(s[a]-s[b]))
#define y(a,b) (dp[a]+s[a]*s[a]-dp[b]-s[b]*s[b])
int main()
{
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        for (int i=0;i<=n;i++)
            s[i]=dp[i]=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&c[i]);
            s[i]=s[i-1]+c[i];
        }
        int l=1,r=1;
        q[r]=0;
        #define j q[r]
        #define k q[l]
        for (int i=1;i<=n;i++)
        {
            while (l<r && y(q[l+1],q[l])<=x(q[l+1],q[l])*s[i])
                l++;
            dp[i]=dp[k]+(s[i]-s[k])*(s[i]-s[k])+m;
            while (l<r && y(q[r],q[r-1])*x(i,q[r])>=y(i,q[r])*x(q[r],q[r-1]))
                r--;
            r++;
            q[r]=i;
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
}
posted @ 2020-08-04 08:31  GK0328  阅读(77)  评论(0编辑  收藏  举报