码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-01-07 17:38:58      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:hnoi2008   down   sdi   print   long   lld   队列   log   ons   

  • 题意

    求将一个长为\(n\)的序列(每个数为\(c_i\))分为很多段,每段(\(i\)~\(j\))的花费是\((j-i+\sum_{k=i}^{j}c_k-L)^2\),求最小的花费。(\(n<=50000\))
  • 题解

    容易看出\(dp\)式子如下
    \(dp[i]=min\{dp[j]+(sum[i]-sum[j]+i-(j+1)-L)^2\} \quad (j < i)\)

    这个式子为\(O(n^2)\)的复杂度,显然过不去,我们进行一下斜率优化就能优化一维枚举决策点的复杂度,变成\(O(n)\)了。

    接下来就需要拆式子,右边有六项,十分的难拆,但我们可以将与\(j\)有关和与\(j\)无关的分开,所以我们可以将这个式子进行一个简单的分割。

    即让\(a[i]\)\(sum[i]+i-1-L\)\(b[j]\)\(sum[j]+j\)

    原式就化为了\(dp[i]=min\{dp[j]+(a[i]-b[j])^2\} \quad (j < i)\)
    \(dp[i]=min\{dp[j]+a[i]^2-a[i]*b[j]+b[j]^2\}\)
    \(j\)\(k\)更优的时候满足\((k<j)\)

    \(dp[j]-2*a[i]*b[j]+b[j]^2<dp[k]-2*a[i]*b[k]+b[k]^2\)

    \((dp[j]+b[j]^2)-(dp[k]+b[k]^2)<2*a[i]*(b[j]-b[k])\)

    \(\frac{(dp[j]+b[j]^2)-(dp[k]+b[k]^2)}{(b[j]-b[k])} < 2*a[i]\)

    然后\(a[i]\)显然满足单调递增。可以用单调队列去维护。

    \(q[Head+1]\)\(q[Head]\)要优,弹出队首。

    然后弹出队尾的时有些麻烦,但结论还是很简单的,如果\(k(q[Tail-1],q[Tail])\)斜率大于\(k(q[Tail],i)\)就可以弹出队尾。(这个可以简单证明一下)

    最后就直接可以每次将队首作为决策转移点去转移了。

  • 代码

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar() ) if (ch == ‘-‘) fh = -1;
    for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ ‘0‘);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
    freopen ("P1010.in", "r", stdin);
    freopen ("P1010.out", "w", stdout);
#endif
}

typedef long long ll;
const int N = 50100;
int n;
ll sum[N], dp[N], L;
ll a[N], b[N];

#define pow2(x) ((x) * (x))
inline ll Dp(int i, int j) {
    return dp[j] + pow2(a[i] - b[j]);
}

inline ll Up(int j, int k) {
    return (dp[j] + pow2(b[j]) ) - (dp[k] + pow2(b[k]) );
}

inline ll Down(int j, int k) {
    return b[j] - b[k];
}

int q[N];
int Head, Tail = 1;

int main () {
    File() ;
    n = read();
    L = read();
    a[0] = - 1 - L;
    For (i, 1, n) {
        sum[i] = sum[i - 1] + read();
        b[i] = sum[i] + i;
        a[i] = b[i] - 1 - L;
    }

    Set(dp, 0x3f); dp[0] = 0;
    For (i, 1, n) {
        while (Head + 1 < Tail && Up(q[Head + 1], q[Head]) <= 2 * a[i] * Down(q[Head + 1], q[Head]) ) ++ Head;
        dp[i] = Dp(i, q[Head]);

        while (Head + 1 < Tail && Up(i, q[Tail - 1]) * Down(q[Tail - 1], q[Tail - 2]) <= Up(q[Tail - 1], q[Tail - 2]) * Down(i, q[Tail - 1]) )  -- Tail;
        q[Tail ++] = i;
    }

    printf ("%lld\n", dp[n]);
    return 0;
}

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

标签:hnoi2008   down   sdi   print   long   lld   队列   log   ons   

原文地址:https://www.cnblogs.com/zjp-shadow/p/8228281.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!