bzoj4586 [Usaco2016 Open]Landscaping

33 篇文章 0 订阅
14 篇文章 0 订阅

Description


农夫约翰正在建造一个美丽的花园,在这个过程中需要移动大量的泥土。花园由N个花圃(1≤N≤100,000)组成,
第i个花圃最开始有Ai个泥土。 农夫约翰想要重新整理花园,使每个花圃最后有Bi个泥土。Ai和Bi都是0…10范围
内的整数。为了整理花园,Farmer John有几个选择:他可以购买一个单位的泥土,并将它放在他选择的花圃中,
用X单位的钱。 他可以从他选择的花圃上清除一块泥土,并用Y单位的钱运出去。他还可以用Z*|i-j|的花费将一单
位的泥土从花圃i运输到花圃j。请计算农民约翰完成他的绿化项目的最低总成本。
0≤X,Y≤10^8; 0≤Z≤1000

Solution


做法非常奇妙
注意到泥土的总数不大,那么可以精确地分配每一单位泥土的去向。

用两个堆h1和h2表示空缺的泥土和多出的泥土
如果要在当前位置i购买一个单位的泥土,就在h2插入i*Z+X,对答案的贡献是Z
如果要在当前位置i清除一个单位的泥土,就在h1插入i*Z+Y,对答案的贡献是Y
如果要从前i个位置中移走一个单位泥土到位置i,就在h2插入i*Z*2-h1.top(),对答案的贡献是i*Z-h1.top(),弹出h1.top()
反之亦然
每一单位泥土都贪心一下

考虑这样做的正确性:|i-j|*Z可以只考虑i< j的情况,也就是i*Z-j*Z。我们插入的每一单位泥土都可以移动到更后的位置,代价柿子展开化简后正好是实际移动的代价

注意两个堆为空的情况和记得开ll

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int N=200005;

int t[N];

std:: priority_queue <LL> h1,h2;

int main(void) {
    freopen("data.in","r",stdin);
    freopen("myp.out","w",stdout);
    int n; LL X,Y,Z; scanf("%d%lld%lld%lld",&n,&X,&Y,&Z);
    rep(i,1,n) {
        int a,b; scanf("%d%d",&a,&b);
        t[i]=a-b;
    }
    LL ans=0;
    for (LL i=1;i<=n;i++) {
        if (t[i]>0) {
            rep(j,1,t[i]) {
                if (h1.empty()) {
                    ans+=Y;
                    h2.push(i*Z+Y);
                } else {
                    if (Y<=i*Z-h1.top()) {
                        ans+=Y;
                        h2.push(i*Z+Y);
                    } else {
                        ans+=i*Z-h1.top(); h2.push(i*Z*2-h1.top());
                        h1.pop();
                    }
                }
            }
        } else if (t[i]<0) {
            rep(j,1,-t[i]) {
                if (h2.empty()) {
                    ans+=X;
                    h1.push(i*Z+X);
                } else {
                    if (X<=i*Z-h2.top()) {
                        ans+=X;
                        h1.push(i*Z+X);
                    } else {
                        ans+=i*Z-h2.top(); h1.push(i*Z*2-h2.top());
                        h2.pop();
                    }
                }
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值