【HNOI2008】玩具装箱

题面


   n5104,0LCi107

解法

  我们可以得出一个 n2 的DP:设 f[i] 表示决策完第 i 件玩具的最小花费,s[i]=ik=1Ci,那么有: f[i]=min { f[j]+(s[i]s[j]+ij1l)2 }
  将里面的式子展开可以得到: f[j]+(s[i]+i)2+(s[j]+j)2+(l+1)22(s[i]+i)(s[j]+j)2(s[i]+i)(l+1)+2(s[j]+j](l+1)
  其中, (s[i]+i)2 (l+1)2 2(s[i]+i)(l+1) 属于和 j 无关的量,直接拉出来,剩下的部分为:f[j]+(s[j]+j)22(s[j]+j)(s[i]+il1)
  假设 i 可以从j以及 k 转移得来,并且从k转移更优,那么有:

f[j]+(s[j]+j)22(s[j]+j)(s[i]+il1)<f[k]+(s[k]+k)22(s[k]+k)(s[i]+il1)

f[j]+(s[j]+j)2f[k](s[k]+k)22[(s[j]+j)(s[k]+k)]<s[i]+il1······

  接下来就只需要根据式①进行斜率优化的过程就可以了

复杂度

O( nlogn

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Rint register int
#define Lint long long int
using namespace std;
const int N=50010;
Lint s[N],f[N];
int q[N],c[N],n,L,head,tail;;
Lint getup(int i,int j)
{
    return f[i]+(s[i]+i)*(s[i]+i)-f[j]-(s[j]+j)*(s[j]+j);
}
Lint getdown(int i,int j)
{
    return 2*(s[i]+i-s[j]-j);
}
bool judge(int i,int j,int C)
{
    Lint A=getup( i,j ),B=getdown( i,j );
    return A<=B*C;
}
bool Judge(int i,int j,int k)
{
    return getup( i,j )*getdown( j,k )<=getup( j,k )*getdown( i,j );
}
Lint cal(int i,int j)
{
    return f[j]+(s[i]-s[j]+i-j-1-L)*(s[i]-s[j]+i-j-1-L);
}
int main()
{
    scanf("%d%d",&n,&L);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&c[i]);
        s[i]=s[i-1]+c[i];
    }
    head=tail=q[0]=0;
    for(int i=1;i<=n;i++)
    {
        while( head+1<=tail && judge( q[head+1],q[head],s[i]+i-L-1 ) )   ++head;
        f[i]=cal( i,q[head] );
        while( head+1<=tail && Judge( i,q[tail],q[tail-1] ) )   tail--;
        q[++tail]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值