本文共 2245 字,大约阅读时间需要 7 分钟。
题意
山脚下有一个锯木厂,有 n n n棵树,只能从山顶往山脚方向走
给出每棵树距离山脚的距离,你可在在任意位置修建两个锯木厂
求所有树运输到锯木厂的花费最小值.
考虑枚举第二个锯木厂建在 i i i位置(编号越小,距离山顶越近)
定义 f [ i ] f[i] f[i]表示第二个锯木厂建在 i i i位置的最小花费
d i s [ i ] dis[i] dis[i]表示运输到山脚的距离和, t o t tot tot为一开始所有树运输到山脚的花费
s u m [ i ] sum[i] sum[i]表示树重量的前缀和(山顶的树在前)
f [ i ] = min { t o t − d i s [ j ] ∗ s u m [ j ] − d i s [ i ] ∗ ( s u m [ i ] − s u m [ j ] ) } f[i]=\min\{tot-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])\} f[i]=min{ tot−dis[j]∗sum[j]−dis[i]∗(sum[i]−sum[j])}其中 ( j < i ) (j<i) (j<i)
如果再次枚举 j j j的位置复杂度是 O ( n 2 ) O(n^2) O(n2)的
f [ i ] = t o t − d i s j ∗ s u m j − d i s i ∗ s u m i + d i s i ∗ s u m j f[i]=tot-dis_j*sum_j-dis_i*sum_i+dis_i*sum_j f[i]=tot−disj∗sumj−disi∗sumi+disi∗sumj
移项得到
d i s i ∗ s u m j − f [ i ] + ( t o t − d i s i ∗ s u m i ) = d i s j ∗ s u m j dis_i*sum_j-f[i]+(tot-dis_i*sum_i)=dis_j*sum_j disi∗sumj−f[i]+(tot−disi∗sumi)=disj∗sumj
把 d i s i dis_i disi看作斜率, s u m j sum_j sumj看作 x x x,把 d i s j ∗ s u m j dis_j*sum_j disj∗sumj看作 y y y
那么就是确定了斜率,求一条直线的最大截距(这样 f [ i ] f[i] f[i]就是最小)
这样我们维护的就是一个上凸壳,而且 d i s i dis_i disi斜率不断变小,所以是单调的
当 s l o p e ( q h e a d , q h e a d + 1 ) slope(q_{head},q_{head+1}) slope(qhead,qhead+1)的斜率仍然大于 d i s i dis_i disi时就不断弹出队列
这样队头就是最优值
然后因为维护的是一个上凸壳,如果把当前的点 i i i加入在队尾,说明有
s l o p e ( q t a i l − 1 , q t a i l ) > = s l o p e ( q t a i l , i ) slope(q_{tail-1},q_{tail})>=slope(q_{tail},i) slope(qtail−1,qtail)>=slope(qtail,i)
不满足,就不断踢出队尾即可
#includeusing namespace std;#define int long longconst int maxn = 1e6+10;const int inf = 1e18;int sum[maxn],dis[maxn],f[maxn],n;int head = 1, tail = 0, q[maxn];int x(int i){ return sum[i]; }int y(int i) { return sum[i]*dis[i]; }double slope(int i,int j){ return ( 1.0*y(i)-y(j) )/( 1.0*x(i)-x(j) );}signed main(){ cin >> n; for(int i=1;i<=n;i++) scanf("%lld%lld",&sum[i],&dis[i] ); for(int i=n;i>=1;i--) dis[i] += dis[i+1]; int ans = inf, tot = 0; for(int i=1;i<=n;i++) tot += dis[i]*sum[i]; for(int i=1;i<=n;i++) sum[i] += sum[i-1]; q[++tail] = 0; for(int i=1;i<=n;i++) { while( head+1<=tail && slope( q[head],q[head+1] )>dis[i] ) head++; int las = q[head]; f[i] = tot-dis[las]*sum[las]-dis[i]*sum[i]+dis[i]*sum[las]; ans = min( ans,f[i] ); while( tail-1>=head && slope( q[tail-1],q[tail] )
转载地址:https://issue-is-vegetable.blog.csdn.net/article/details/116263082 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!