JZOJ 5043. 【NOI2017模拟4.4】保持平衡
2017-10-21 21:57
344 查看
题目大意
博爱路上种起了一棵棵的大树,但是有一些地方的树超过了负荷,有一些地方的树的数量又不够。我们不妨把博爱路看做一条数轴,数轴有n个点,从1到n编号,第i个位置原来现在有ai棵树,这个位置的需求是bi棵树。ai,bi都是0到10的整数。由于你需要是这个位置的树的数量保持平衡,所以你需要移除或者搬一些树过来。
我们怎么使树的数量平衡呢?
首先,你可以从某个位置i移动一棵树到位置j,这时,你需要的运费是|i-j|*z元。
其次,你可以从商店买一棵树,需要支付x元,这时商店会把树配送到任意位置。
还有就是,你可以叫别人收购在任意位置一棵树,需要支付y元运费。
问使得树的数量平衡最小需要支付多少钱?
数据范围
对于全部数据:n≤100000,0≤x,y≤10^8,0≤z≤1000,0≤ai,bi≤10。
解法
条件:①每一个地方可以种树或挖树,可以放到其他地方。
②一个等式:|i−j|∗z。可以考虑拆开它。
要求什么:最小的代价。
寻找突破口:有一个很显然的结论,从单独左往右做或者单独从右往左做是最优的,否则一定会出现重叠的情况。有重叠的部分就一定不是最优的。
我们从左往右搞。
可以将|i-j|*z看作i*z-j*z。建立两个堆,一个need堆,一个noneed堆。
need堆里的每一个元素都代表着以后和一个i*z匹配成的状态的一部分,为以后需要种树的地方提供条件。
noneed堆里的每一个元素,为以后需要挖树的地方提供条件。
我们要满足贪心的话,就要使局部最优解代表全局最优解。
考虑要种树的情况。
由于待种(挖)的树来源不明,所以不能一起做,只能一棵一棵处理。
如果在j这个地方有一棵树被挖走了,付出的代价为T,则在need堆放入-j*z-T
做到i(i>j)的时候,我们在need堆里面找出最小的元素,若它为-j*z-T,则这次付出的代价为i*z-j*z-T。
这样子确保了在1~i的点树的分布情况是使得答案最优的。
者表示什么呢?我在j挖走了一棵树,现在放到了i去,那么原本的代价T被抵消了。而现在使得1~i的树的情况是局部最优解,也是暂时的全局最优解。
挖树的情况同理。
总结一下:
①如果贪心要使当前的解为局部最优解,且要牺牲之前的一个操作,那么考虑可撤销的贪心:将当前的操作付出的代价绑到能更新后面的最优解的变量里面去。
②看清数据的范围,可以改变一下思维方式。比如说待种(挖)的树之前的位置是不知道的,那么看下能否一棵一棵处理。
代码
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define N 100005 #define M 1000005 #define LL long long #define fo(i,a,b) for(i=a;i<=b;i++) using namespace std; struct hp{ LL a[M]; LL tot; };hp noned,ned; LL i,j,k,l,n,m,temp; LL x,y,z,a ,b ; LL ans; void heap_down(hp &heap,LL x){ while (1){ int l=x*2,r=x*2+1,mx; if (l>heap.tot) break; mx=l; if (heap.a[r]<heap.a[mx] && r<=heap.tot) mx=r; if (heap.a[x]<=heap.a[mx]) break; swap(heap.a[x],heap.a[mx]); x=mx; } } void heap_up(hp &heap,LL x){ while (1){ if (x==1) break; if (heap.a[x]>=heap.a[x/2]) break; swap(heap.a[x],heap.a[x/2]); x/=2; } } void heap_push(hp &heap,LL x){ heap.a[++heap.tot]=x; heap_up(heap,heap.tot); } void heap_pop(hp &heap){ heap.a[1]=heap.a[heap.tot]; heap.tot--; heap_down(heap,1); } int main(){ freopen("balance.in","r",stdin); freopen("balance.out","w",stdout); scanf("%lld%lld%lld%lld",&n,&x,&y,&z); fo(i,1,n) scanf("%lld%lld",&a[i],&b[i]); fo(i,1,n){ if (a[i]>b[i]){ fo(j,1,a[i]-b[i]){ if (noned.tot>0 && i*z+noned.a[1]<y) temp=i*z+noned.a[1],heap_pop(noned); else temp=y; ans+=temp; heap_push(ned,-z*i-temp); } } else if (a[i]<b[i]){ fo(j,1,b[i]-a[i]){ if (ned.tot>0 && i*z+ned.a[1]<x) temp=i*z+ned.a[1],heap_pop(ned); else temp=x; ans+=temp; heap_push(noned,-i*z-temp); } } } printf("%lld",ans); return 0; }
相关文章推荐
- 【JZOJ5043】【NOI2017模拟4.4】保持平衡
- jzoj5043 【NOI2017模拟4.4】保持平衡 (可撤销贪心)
- 5043. 【NOI2017模拟4.4】保持平衡
- 【NOI2017模拟4.4】保持平衡
- 【NOI2017模拟4.4】保持平衡【优先队列,贪心】
- [JZOJ5044]【NOI2017模拟4.4】Sone0
- [JZOJ5029]. 【NOI2017模拟3.25】围墙
- JZOJ【NOI2017模拟3.30】原谅
- 【JZOJ5040】【NOI2017模拟4.2】押韵
- 【JZOJ5045】【NOI2017模拟4.5】无限棋盘
- 【JZOJ5039】【NOI2017模拟4.2】查询
- [JZOJ100003]【NOI2017模拟.4.1】 Tree
- 【JZOJ5046】【NOI2017模拟4.5】机器人游戏
- [JZOJ100004]【NOI2017模拟.4.1】 Dice
- 【JZOJ 100019】【NOI2017模拟6.26】A
- [JZOJ4426]. 【HNOI2016模拟4.4】Stage
- 【JZOJ5036】【NOI2017模拟3.30】原谅
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- 【JZOJ5037】【NOI2017模拟3.30】轮回
- JZOJ 100019【NOI2017模拟6.26】A