• [BZOJ1096] [ZJOI2007] 仓库建设 (斜率优化)


    Description

      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公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。

    Input

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

    Output

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

    Sample Input

    3
    0 5 10
    5 3 100
    9 6 10

    Sample Output

    32

    HINT

      在工厂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位带符号整数。 

    Source

    Solution

      设$f[i]$表示在第$i$个工厂建仓库时前$i$个工厂的最小花费,则:

      $displaystyle f[i]=minleft{f[j]+sum_{k=j+1}^{i}(c[i]-c[k])*p[k] ight}+c[i]$

        $displaystyle=minleft{f[j]+c[i]*sum_{k=j+1}^{i}p[k]-sum_{k=j+1}^{i}c[k]*p[k] ight}+c[i]$

      令$displaystyle sump[i]=sum_{j=1}^i p[j]$,$displaystyle sumxp[i]=sum_{j=1}^i x[j]*p[j]$,则:

      $displaystyle f[i]=minig{ f[j]+x[i]*(sump[i]-sump[j])-(sumxp[i]-sumxp[j]) ig}+c[i]$

      据说$f[i]$满足决策单调性,辣么这一步的证明跳过= =b

      设$j<k$且$k$比$j$优,那么最后化成的斜率式是这样的:$displaystylefrac{(f[k]+sumxp[k])-(f[j]+sumxp[j])}{sump[k]-sump[j]}<c[i]$

      维护下凸壳搞一搞就行了

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 int x[1000005], p[1000005], c[1000005], q[1000005];
     5 ll sump[1000005], sumxp[1000005], f[1000005];
     6 
     7 double slope(int i)
     8 {
     9     return 1.0 * (f[q[i]] + sumxp[q[i]] - f[q[i - 1]] - sumxp[q[i - 1]]) / (sump[q[i]] - sump[q[i - 1]]);
    10 }
    11 
    12 int main()
    13 {
    14     int n, front = 0, back = 1;
    15     scanf("%d", &n);
    16     for(int i = 1; i <= n; ++i)
    17     {
    18         scanf("%d%d%d", x + i, p + i, c + i);
    19         sump[i] = sump[i - 1] + p[i];
    20         sumxp[i] = sumxp[i - 1] + (ll)x[i] * p[i];
    21     }
    22     for(int i = 1; i <= n; ++i)
    23     {
    24         while(front < back - 1 && slope(front + 2) < x[i])
    25             ++front;
    26         int j = q[front + 1];
    27         f[i] = f[j] + x[i] * (sump[i] - sump[j]) - sumxp[i] + sumxp[j] + c[i];
    28         q[++back] = i;
    29         while(front < back - 2 && slope(back) < slope(back - 1))
    30             q[--back] = i;
    31     }
    32     printf("%lld
    ", f[n]);
    33     return 0;
    34 }
    View Code
  • 相关阅读:
    shell脚本-awk
    shell脚本-sed命令
    shell脚本-grep和正则表达式
    wuti
    dmesg、stat命令
    uname、hostname命令
    tee、vi/vim命令
    tr、od命令
    vimdiff、rev命令
    dos2unix、diff命令
  • 原文地址:https://www.cnblogs.com/CtrlCV/p/5622305.html
Copyright © 2020-2023  润新知