BZOJ 1010: [HNOI2008]玩具装箱toy

14 篇文章 0 订阅
3 篇文章 0 订阅

dp ~
f[i]=min{f[j]+(sum[i]sum[j]+ij1L)2}
这显然是 O(n2) 啃腚会 T
写个小小的斜率优化
j>k,g[i]=sum[i]+i
f[j]+(g[i]g[j]1L)2f[k](g[i]g[k]1L)2<0 时,我们显然会选择 j
变形一下得

f[j]f[k]+(2g[i]g[k]g[j]2L2)(g[k]g[j])<0

再变

f[j]f[k]g[j]g[k]+g[j]+g[k]<2(g[i]L1

可以发现右边那个式子是不下降的,左边就是我们要的斜率
所以应该维护斜率不下降,否则就是前面的不够优,可以被后面的白吃
出队列时同样判断前两个点斜率是否比 2(g[i]L1) 小,小的话就是后面更好,前面的出队列
搞清楚后 1A 也是挺容易的


代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define gg getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=gg;
    for(;ch<'0'||ch>'9';ch=gg)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=gg)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],t=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++t]=x%10;
    for(int i=t;i;--i)putchar('0'+a[i]);
    if(t==0)putchar('0');
    putchar('\n');
}
int tou,wei;
ll f[50050],dui[50050],sum[50050],n,L,le[50050];
inline ll sqr(ll x){return x*x;}
inline ll g(ll x){return sum[x]+x;}
inline ll xie(ll j,ll k){return (f[j]-f[k])/(g(j)-g(k))+g(k)+g(j);} //斜率
void push(int x){
    for(;tou<wei;--wei){
        if(xie(x,dui[wei])>xie(dui[wei],dui[wei-1]))break;
    }
    dui[++wei]=x;
}
void pop(int m){
    for(;tou<wei;++tou){
        int k=dui[tou],j=dui[tou+1];
        if(xie(j,k)>m)break;
    }
}
int main(){
    n=read();L=read();
    for(int i=1;i<=n;++i)le[i]=read(),sum[i]=sum[i-1]+le[i];
    f[0]=0;
    f[1]=sqr(le[1]-L);
    dui[tou=0]=0;
    dui[wei=1]=1;
    for(int i=2;i<=n;++i){
        pop(2*(g(i)-L-1));
        f[i]=f[dui[tou]]+sqr(g(i)-g(dui[tou])-L-1);
        push(i);
    }
    out(f[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值