【bzoj1096】[ZJOI2007]仓库建设

*题目描述:

  L公司有N个工厂,由高到底分布在一座山上。如图所示,工厂1在山顶,工厂N在山脚。由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,假设一件产品运送1个单位距离的费用是1。假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到
以下数据:1:工厂i距离工厂1的距离Xi(其中X1=0);2:工厂i目前已有成品数量Pi;:3:在工厂i建立仓库的费用Ci;请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。

*输入:
  第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。

*输出:
  仅包含一个整数,为可以找到最优方案的费用。

*样例输入:
3
0 5 10
5 3 100
9 6 10

*样例输出:
32

*提示:
在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。
【数据规模】
对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。

*题解:
斜率优化。
首先我们很容易地可以列出DP方程:
fi=mini1j=1(fj+calc(j+1,i)+ci)
calc(j,i) 表示区间[j,i] 内的所有货物全部运送到i节点所需要的代价,fi 表示第i号节点建成仓库所需要的最小代价。
这里如果计算calc(j,i) 的值我是参考hzwer学长的题解的,丢链接跑。
首先我们先假设所有的货物都从1号节点出发,计sumipi 的前缀和,那么区间内的货物从1号节点到i号节点的费用就是(sumisumj)xi 。然后每个节点的货物可以少运送的费用为xipi 。于是我们计bixipi 的前缀和。然后DP的转移方程就变成了:
fi=mini1j=1(fj+(sumisumj)xi(bibj)+ci)
将式子稍微转化即可变成我们熟悉的斜率优化的形式:
fi=sumixi+cibi+mini1j=1(fj+bjsumjxi)
比较斜率优化的式子yjkixj,此时,fj+bj 就是点的纵坐标 yjsumj 就是点横坐标 xjxi 就是斜率ki。又因为这题里面斜率ki 和横坐标xj 都是单调的,所以我们可以用单调队列来维护凸包,复杂度O(n)。

*代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

#ifdef WIN32
    #define LL "%I64d"
#else
    #define LL "%lld"
#endif

#ifdef CT
    #define debug(...) printf(__VA_ARGS__)
    #define setfile() 
#else
    #define debug(...)
    #define filename ""
    #define setfile() freopen(filename".in", "r", stdin); freopen(filename".out", "w", stdout)
#endif

#define R register
#define getc() (S == T && (T = (S = B) + fread(B, 1, 1 << 15, stdin), S == T) ? EOF : *S++)
#define dmax(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
#define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
#define cabs(_x) ((_x) < 0 ? (- (_x)) : (_x))
char B[1 << 15], *S = B, *T = B;
inline int F()
{
    R char ch; R int cnt = 0; R bool minus = 0;
    while (ch = getc(), (ch < '0' || ch > '9') && ch != '-') ;
    ch == '-' ? minus = 1 : cnt = ch - '0';
    while (ch = getc(), ch >= '0' && ch <= '9') cnt = cnt * 10 + ch - '0';
    return minus ? -cnt : cnt;
}
#define maxn 1000010
int x[maxn], p[maxn], c[maxn], q[maxn];
long long b[maxn], sum[maxn], f[maxn];
#define x(_i) (sum[_i])
#define y(_i) (f[_i] + b[_i])
inline double slope(R int i, R int j)
{
    return (double) (y(i) - y(j)) / (x(i) - x(j));
}
int main()
{
//  setfile();
    R int n = F();
    for (R int i = 1; i <= n; ++i)
    {
        x[i] = F();
        p[i] = F();
        c[i] = F();
        sum[i] = sum[i - 1] + p[i];
        b[i] = b[i - 1] + 1ll * p[i] * x[i];
    }
/*
    memset(f, 63, sizeof (f)); f[0] = 0;
    for (R int i = 1; i <= n; ++i)
    {
        for (R int j = 0; j < i; ++j)
            cmin(f[i], f[j] + (sum[i - 1] - sum[j]) * x[i] - b[i - 1] + b[j] + c[i]);
    }
*/
    R int h = 1, t = 1;
    q[h] = 0;
    for (R int i = 1; i <= n; ++i)
    {
        while (h < t && slope(q[h], q[h + 1]) < x[i]) ++h;
        R int j = q[h];
        f[i] = f[j] + (sum[i] - sum[j]) * x[i] - b[i] + b[j] + c[i];
        while (h < t && slope(q[t - 1], i) < slope(q[t - 1], q[t])) --t;
        q[++t] = i;
    }
    printf("%lld\n", f[n] );
    return 0;
}
/*
3
0 5 10
5 3 100
9 6 10
*/
posted @ 2016-09-10 13:45  cot  阅读(215)  评论(0编辑  收藏  举报