bzoj4596&&JZOJ5271&&5043保持平衡 贪心+堆(经典思想)

来源:互联网 发布:ubuntu skype安装包 编辑:程序博客网 时间:2024/06/03 11:16
博爱路上种起了一棵棵的大树,但是有一些地方的树超过了负荷,有一些地方的树的数量又不够。我们不妨把博爱路看做一条数轴,数轴有n个点,从1到n编号,第i个位置原来现在有ai棵树,这个位置的需求是bi棵树。ai,bi都是010的整数。由于你需要是这个位置的树的数量保持平衡,所以你需要移除或者搬一些树过来。我们怎么使树的数量平衡呢?首先,你可以从某个位置i移动一棵树到位置j,这时,你需要的运费是|i-j|*z元。其次,你可以从商店买一棵树,需要支付x元,这时商店会把树配送到任意位置。还有就是,你可以叫别人收购在任意位置一棵树,需要支付y元运费。问使得树的数量平衡最小需要支付多少钱?

至于5271,基本意思差不多,但是每个位置只有0/1两种情况,而且没有收购(y元)的操作,同时z=1。
这是一类经典的贪心操作,比赛居然没打出来,简直无语。。看来之前就没掌握好。
对于每一个点,我们可以发现,他的树如果只能从其他地方移动过来,只能是从他前面多余的树,如果是后面的,可以转移到前面来。
那么我们把代价拆开,即(i-j)z=i*z-j*z(i>j)
然后对于一个点,他要获得树,只有两种可能:从其他地方购买花费x,或者从其他地方移动花费i*z-j*z。
那么我们维护两个堆,need和noneed,表示供应和需要堆。
假设我在j位置有1棵树被移走了,那么我们就在noneed堆内加入代价-j*z-pay,pay是之前的代价,那么我们为什么要这么加入呢?
假设我在i位置需要一棵树,此时供应堆顶是-j*z-pay,那么我的答案就是:
i*z+(-j*z-pay),这就相当于把之前的代价撤销,把j这颗树移到我这边来。
最后在需求堆内加入代价-i*z-pay。
由于堆最小的性质可以达到最优的要求。
同时%%%wxh的dp做法:http://blog.csdn.net/wxh010910/article/details/64920634

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)#include<queue> using namespace std;typedef long long ll;int n,x,y,z;ll ans;priority_queue<int,vector<int>,greater<int> >q1,q2;inline void solve(){    fo(i,1,n)    {        int a,b;        scanf("%d%d",&a,&b);        int tmp;        for(;a>b;a--)        {            tmp=y;            if (!q1.empty()&&q1.top()+i*z<y)            {                tmp=q1.top()+i*z;                q1.pop();            }            ans+=tmp;            q2.push(-i*z-tmp);        }        for(;a<b;a++)        {            tmp=x;            if (!q2.empty()&&q2.top()+i*z<x)            {                tmp=q2.top()+i*z;                q2.pop();            }            ans+=tmp;            q1.push(-i*z-tmp);        }    }    printf("%lld\n",ans);}inline void solve1(){    fo(i,1,n)    {    }}int main(){    scanf("%d%d%d%d",&n,&x,&y,&z);    solve();    return 0;}