P4360 [CEOI2004]锯木厂选址(枚举+斜率优化)
发布日期:2021-06-30 10:32:42 浏览次数:2 分类:技术文章

本文共 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{

totdis[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]=totdisjsumjdisisumi+disisumj

移项得到

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 disisumjf[i]+(totdisisumi)=disjsumj

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 disjsumj看作 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(qtail1,qtail)>=slope(qtail,i)

不满足,就不断踢出队尾即可

#include 
using 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:P3628 [APIO2010]特别行动队(简单斜率优化)
下一篇:P2627 [USACO11OPEN]Mowing the Lawn G(单调队列优化dp)

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月11日 18时13分28秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章