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

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/82432884

传送门
一道经典的斜率优化dp。
推式子ing。。。
令f[i]表示装前i个玩具的最优代价。
然后用老套路。
我们只考虑把第 j + 1 ~ i 个玩具分成一组的情况,之前的1~j个自行按最优情况分组。
显然有 f [ i ] = f [ j ] + ( s u m [ i ] s u m [ j ] + L ) 2
那么对于决策j,k。
谁对i的贡献更优呢?
我们假设j更优。
显然有
f [ i ] = f [ j ] + ( s u m [ i ] s u m [ j ] + L ) 2 < f [ i ] = f [ k ] + ( s u m [ i ] s u m [ k ] + L ) 2
=> f [ j ] + s u m [ j ] 2 2 s u m [ i ] s u m [ j ] < f [ k ] + s u m [ k ] 2 2 s u m [ i ] s u m [ k ]
T [ t ] = f [ t ] + s u m [ t ] 2
=> ( T [ j ] T [ k ] ) / ( s u m [ j ] s u m [ k ] ) < 2 s u m [ i ]
这不就是斜率优化么?
用队列维护一下就行了。
继续分析会发现应该维护单调递增的斜率,也就是维护一个下凸壳。
代码:

#include<bits/stdc++.h>
#define ll long long
#define N 50005
using namespace std;
inline ll read(){
    ll ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,hd,tl,q[N];
ll L,sum[N],f[N];
inline ll gety(int i,int j){return f[i]-f[j]+sum[i]*sum[i]-sum[j]*sum[j];}
inline ll getx(int i,int j){return sum[i]-sum[j];}
inline double slope(int i,int j){return 1.0*gety(i,j)/getx(i,j);}
int main(){
    n=read(),L=read()+1,hd=tl=1;
    for(int i=1;i<=n;++i)sum[i]=sum[i-1]+read();
    for(int i=1;i<=n;++i){
        sum[i]+=i;
        while(hd<tl&&slope(q[hd+1],q[hd])<=2*(sum[i]-L))++hd;
        f[i]=f[q[hd]]+(sum[i]-L-sum[q[hd]])*(sum[i]-L-sum[q[hd]]);
        while(hd<tl&&slope(q[tl],q[tl-1])>slope(i,q[tl]))--tl;
        q[++tl]=i;
    }
    cout<<f[n];
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/82432884