您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: