[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]=min\left\{f[j]+\sum_{k=j+1}^{i}(c[i]-c[k])*p[k]\right\}+c[i]$

    $\displaystyle=min\left\{f[j]+c[i]*\sum_{k=j+1}^{i}p[k]-\sum_{k=j+1}^{i}c[k]*p[k]\right\}+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]=min\big\{\ f[j]+x[i]*(sump[i]-sump[j])-(sumxp[i]-sumxp[j])\ \big\}+c[i]$

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

  设$j<k$且$k$比$j$优,那么最后化成的斜率式是这样的:$\displaystyle\frac{(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\n", f[n]);
33     return 0;
34 }
View Code

 

posted @ 2016-06-28 10:17  CtrlCV  阅读(260)  评论(0编辑  收藏  举报