bzoj3437 小P的牧场
3437: 小P的牧场
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1643 Solved: 891
[Submit][Status][Discuss]
Description
小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧场一直到它西边第一个控制站的所有牧场(它西边第一个控制站所在的牧场不被控制)(如果它西边不存在控制站,那么它控制西边所有的牧场),每个牧场被控制都需要一定的花费(毕竟在控制站到牧场间修建道路是需要资源的嘛~),而且该花费等于它到控制它的控制站之间的牧场数目(不包括自身,但包括控制站所在牧场)乘上该牧场的放养量,在第i个牧场建立控制站的花费是ai,每个牧场i的放养量是bi,理所当然,小P需要总花费最小,但是小P的智商有点不够用了,所以这个最小总花费就由你来算出啦。
Input
第一行一个整数 n 表示牧场数目
第二行包括n个整数,第i个整数表示ai
第三行包括n个整数,第i个整数表示bi
Output
只有一行,包括一个整数,表示最小花费
Sample Input
2424
3142
Sample Output
样例解释
选取牧场1,3,4建立控制站,最小费用为2+(2+1*1)+4=9。
1<=n<=1000000, 0 < a i ,bi < = 10000
Source
分析:用斜率优化,和bzoj3156差不多,都是需要预处理几个前缀.
预处理3个数组:sum,d,num,分别表示从右往左数到i的d的后缀和、b的后缀和、有多少个数.稍微推一推就能写出式子.
重点想说一说的是斜率优化中该用哪两个比较符号.如果要求最大值,那么每次就要将完全被覆盖在下面的直线给丢掉,如果要求最小值,就把上面没有被覆盖的直线给丢掉, 这两种情况丢掉的直线是永远都取不到最优值的直线. 至于丢掉直线的前提条件,就要看维护的到底是上凸壳还是下凸壳了.
上面说的是队尾的处理,队首怎么处理呢? 这个根据题目要求的到底是最大值还是最小值来决定.如果要求的是最大值,那么在自变量为x的位置第l条直线比第l+1条直线的纵坐标要小,就把第l条直线丢掉. 因为斜率是递增的,现在小了以后肯定还会小. 求最大值的话取个相反的即可. 如果出现单调性与题目要求的相矛盾,应该是优化不了的.
也就是说,该用哪两个比较符号取决于斜率的单调性以及问题要求什么.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 1000010; ll n,a[maxn],b[maxn],num[maxn],sum[maxn],d[maxn],l,r,q[maxn],f[maxn]; ll K(ll i) { return -d[i + 1]; } ll B(ll i) { return sum[i + 1] + f[i]; } ll Y(ll i,ll j) { return K(i) * num[j] + B(i); } bool cmp(ll y1,ll y2,ll y3) { ll temp1 = (K(y1) - K(y3)) * (B(y2) - B(y1)); ll temp2 = (K(y1) - K(y2)) * (B(y3) - B(y1)); return temp1 <= temp2; //主要是这里的符号问题. 如果斜率递减(不是绝对值),那么这里的符号就和下面标1位置的符号相同,否则相反 } int main() { scanf("%lld",&n); for (ll i = 1; i <= n; i++) scanf("%lld",&a[i]); for (ll i = 1; i <= n; i++) scanf("%lld",&b[i]); for (ll i = n - 1; i >= 1; i--) num[i] = num[i + 1] + 1; for (ll i = n; i >= 1; i--) d[i] = d[i + 1] + b[i]; for (ll i = n; i >= 1; i--) sum[i] = sum[i + 1] + b[i] * num[i]; for (ll i = 1; i <= n; i++) { while (l < r && Y(q[l],i) >= Y(q[l + 1],i)) //1 l++; f[i] = Y(q[l],i) + num[i] * d[i] + a[i] - sum[i]; while (l < r && cmp(i,q[r - 1],q[r])) r--; q[++r] = i; } printf("%lld\n",f[n]); return 0; }