首页 > 代码库 > bzoj1010: [HNOI2008]玩具装箱toy(斜率优化DP)

bzoj1010: [HNOI2008]玩具装箱toy(斜率优化DP)

  codevs也有这题,伪·双倍经验233

  首先朴素DP方程很容易看出:f[i]=min(f[j]+(i-j-1+sum[i]-sum[j]-L)^2);

  于是设g[i]=i+sum[i]

     g[j]=j+sum[j] 

     c=1+L

  则f[i]=min(f[j]+(g[i]-g[j]-c)^2)

  证明决策单调性,假设 j 比 k 优  

     f[j]+(g[i]-g[j]-c)^2<f[k]+(g[i]-g[k]-c)^2

   证明f[j]+(g[x]-g[j]-c)^2<f[k]+(g[x]-g[k]-c)^2

     f[j]+(g[i]+y-g[j]-c)^2<f[k]+(g[i]+y-g[k]-c)^2

     f[j]+(g[i]-g[j]-c)^2-2*y*(g[i]-g[j]-c)<f[k]+(g[i]-g[k]-c)^2-2*y*(g[i]-g[k]-c)

     g[j]>g[k]

  所以当 j > k 时,j 比 k 优,则 j 一直比 k 优。

  然后就可以斜率优化啦

     f[j]+(g[i]-g[j]-c)^2<f[k]+(g[i]-g[k]-c)^2

     f[j]-2*g[i]*(g[j]+c)+(g[j]+c)^2<f[k]-2*g[i]*(g[k]+c)+(g[k]+c)^2  

     (f[j]+(g[j]+c)^2-f[k]-(g[k]+c)^2)/(2*(g[k]-g[j]))<g[i]

  所以斜率是递增的,维护个下凸包

技术分享
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=500010;
int l,r;
ll n,L,sum[maxn],g[maxn],q[maxn],f[maxn];
void read(ll &k)
{
    int f=1;k=0;char c=getchar();
    while(c<0||c>9)c==-&&(f=-1),c=getchar();
    while(c<=9&&c>=0)k=k*10+c-0,c=getchar();
    k*=f;    
} 
ll sqr(ll x){return x*x;}
ll xl(int j,int k){return (f[j]+sqr(g[j]+1+L)-f[k]-sqr(g[k]+1+L))/(2*(g[j]-g[k]));}
int main()
{
    read(n);read(L);
    for(int i=1;i<=n;i++)
    read(sum[i]),sum[i]+=sum[i-1];
    for(int i=1;i<=n;i++)
    {
        g[i]=i+sum[i];
        while(l<r&&xl(q[l],q[l+1])<g[i])l++;
        f[i]=f[q[l]]+sqr(g[i]-g[q[l]]-1-L);
        while(l<r&&xl(q[r],q[r-1])>xl(i,q[r]))r--;
        q[++r]=i;
    }
    printf("%lld\n",f[n]);
}
View Code 

 

bzoj1010: [HNOI2008]玩具装箱toy(斜率优化DP)