[HDU 3507] Print Article

来源:互联网 发布:相对湿度数据 编辑:程序博客网 时间:2024/06/02 02:19

Problem Link

    这题应该可以算是斜率优化dp的入门题了,为了巩固斜率优化dp的方法,我特此写一篇题解表明我是真懂了。

Description

    给定nm以及一个含n个元素的序列,要求依次取完所有的数,每次取一段连续的数的花费是,这一段中所有数之和的平方加上m,求取完所有数的最小花费。

Solution

    这道题显然是道dp题,而且O(n2)dp还是蛮好想的,定义dp[i]表示取到第i个数时的最小花费,那么dp转移方程也就很明显了:

dp[i]=Mini1j=0{dp[j]+(sum[i]sum[j])2+m}

    然而O(n2)肯定要炸,n可是有500000呐,那怎么办呢,这里就要用到斜率优化的方法将O(n2)转化为O(n)

    k<j<i,假如jk优的话,那么就是满足

dp[k]+(sum[i]sum[k])2+mdp[j]+(sum[i]sum[j])2
将平方展开并移项可以得到
(dp[j]+sum[j]2)(dp[k]+sum[k]2)2(sum[j]sum[k])sum[i]

    也就是说,只要满足了这个条件,j就一定比k优,又因为sum[i]是递增的,所以这个不等式在后来也一定成立, 因此k可以直接淘汰了。假如我们令xj=2sum[j]yj=dp[j]+sum[j]2xk=2sum[k]yk=dp[k]+sum[k]2,那么不等式左边就可以表示为yjykxjxk,这就变成了斜率,我们不妨定义f(k,j)表示上述表达式,那么我们可以得到两个推论(在k<j<i的情况下):

    1.当f(k,j)sum[i]就说明jk优,反之kj优。因此当f(k,j)sum[i]时我们可以直接淘汰k

        2.假如f(k,j)<f(j,i)的话,我们可以淘汰掉j。因为当f(j,i)sum[i]时,ij更优,所以可以淘汰j;当f(j,i)>sum[i]时,f(k,j)>sum[i],说明kj更优。

    我们在dp的过程中维护一个单调队列,实时维护最优值即可。

Code

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>#define N 500005#define ll long longusing namespace std;template <class T>inline void Rd(T &res){    char c;res=0;int k=1;    while(c=getchar(),c<48&&c!='-');    if(c=='-'){k=-1;c='0';}    do{        res=(res<<3)+(res<<1)+(c^48);    }while(c=getchar(),c>=48);    res*=k;}template <class T>inline void Pt(T res){    if(res<0){        putchar('-');        res=-res;    }    if(res>=10)Pt(res/10);    putchar(res%10+48);}int n,m;int A[N];int dp[N];int x[N],y[N];int Q[N];int getup(int k,int j){    return y[j]-y[k];}int getdown(int k,int j){    return x[j]-x[k];}int calc(int j,int i){    return dp[j]+(A[i]-A[j])*(A[i]-A[j])+m;}int main(){    while(scanf("%d",&n)!=EOF){        Rd(m);        for(int i=1;i<=n;i++){            Rd(A[i]);            A[i]+=A[i-1];        }        int L=0,R=0;        Q[R++]=0;        dp[0]=0;        for(int i=1;i<=n;i++){            while(L+1<R&&getup(Q[L],Q[L+1])<=getdown(Q[L],Q[L+1])*A[i])L++;            dp[i]=calc(Q[L],i);            x[i]=2*A[i];            y[i]=dp[i]+A[i]*A[i];            while(L+1<R&&getup(Q[R-1],i)*getdown(Q[R-2],Q[R-1])<=getup(Q[R-2],Q[R-1])*getdown(Q[R-1],i))R--;            Q[R++]=i;        }        Pt(dp[n]);        putchar('\n');    }    return 0;}
原创粉丝点击