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

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1096

一开始想了想费用流,然后被数据范围pass掉了,感觉dp更可行一些。

只想到一个O(n2)的做法,看到式子比较复杂,就感觉像是斜率优化。

dp[i]表示前i个工厂所求的最小费用,则第i个工厂一定会建一个仓库。转移方程为dp[i]=min(dp[j]+\sum_{k=j}^{i-1}p[k]*(x[i]-x[k]))+c[i]。

将式子展开,用sump表示p数组的前缀和,sumpx表示p数组*x数组的前缀和。

方程就变成了dp[i]=min(dp[j] + c[i] + (sumx[i - 1] - sumx[j])*x[i] - (sumpx[i - 1] - sumpx[j]))

然后设k<j<i时,存在从j转移到i比从k转移到i更优。

则dp[j] + c[i] + (sumx[i - 1] - sumx[j])*x[i] - (sumpx[i - 1] - sumpx[j])<dp[k] + c[i] + (sumx[i - 1] - sumx[k])*x[i] - (sumpx[i - 1] - sumpx[k])

移项展开可以得到dp[j] + sumpx[j] - (dp[k] + sumpd[k]) < (sump[j] - sump[k])*x[i]

设f[i]=dp[i]+sumpx[i],T[i]=sump[i]。

则上式变成(f[j]-f[k])/(T[j]-T[k])<x[i]。这就是经典的斜率方程

扫描二维码关注公众号,回复: 6721763 查看本文章

在每次求dp[i]时我们已经得出了dp[j]和dp[k],则可以得到二维坐标(T[j],f[j]),(T[k],f[k])。

会发现,如果还存在一个点y,y<k,且(f[k]-f[y])/(T[k]-T[y])>=x[i],则k点可以直接被pass掉,在二维坐标中,就是维护一个下凸包点集。

这样就可以使得每次最优转移点可以O(1)得到,程序复杂度为O(n)。

 1 #include<bits/stdc++.h>
 2 #define lson l,mid,i<<1
 3 #define rson mid+1,r,i<<1|1
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn = 1e6 + 10;
 7 ll c[maxn], x[maxn], p[maxn];
 8 ll sump[maxn];
 9 ll sumpx[maxn];
10 ll dp[maxn];
11 ll q[maxn];
12 ll check1(int j, int k) {
13     return dp[j] + sumpx[j] - dp[k] - sumpx[k];
14 }
15 ll check2(int j, int k) {
16     return sump[j] - sump[k];
17 }
18 int main() {
19     int n;
20     scanf("%d", &n);
21     for (int i = 1; i <= n; i++)
22         scanf("%lld%lld%lld", &x[i], &p[i], &c[i]);
23     for (int i = 1; i <= n; i++)
24         sump[i] = sump[i - 1] + p[i], sumpx[i] = sumpx[i - 1] + p[i] * x[i];
25     int l = 1, r = 1;
26     q[l] = 0;
27     for (int i = 1; i <= n; i++) {
28         while (l < r&&check1(q[l + 1], q[l]) <= x[i] * check2(q[l + 1], q[l]))
29             l++;
30         dp[i] = dp[q[l]] + c[i] + (sump[i - 1] - sump[q[l]])*x[i] - (sumpx[i - 1] - sumpx[q[l]]);
31         while (l < r&&check1(q[r], q[r - 1])*check2(i, q[r]) >= check1(i, q[r])*check2(q[r], q[r - 1]))
32             r--;
33         q[++r] = i;
34     }
35     printf("%lld\n", dp[n]);
36     return 0;
37 }
View Code

 

猜你喜欢

转载自www.cnblogs.com/sainsist/p/11130188.html