题解 P2748 【[USACO16OPEN]Landscaping P】
因为
对于每单位泥土在第
-
如果是少了泥土,可以花费
X 费用来解决,所以V_i=X ,还可以向前面要泥土,要泥土一定向之前多泥土的地方要,要花费Z|i-j| 费用,但之前的泥土我们已经考虑了它的贡献了,所以之前的泥土的贡献就要再减去(即之前的那个泥土多了,但是不需要处理了,后面少了的那个泥土直接要过来了),总的费用为Z|i-j|-V_j ,所以V_i=\min(X,Z|i-j|-V_j) -
如果是多了泥土,可以花费
Y 费用来解决,所以V_i=Y 。还可以往前面送泥土,送泥土一点向之前少泥土的地方送。同理,总的费用为Z|i-j|-V_j ,所以V_i=\min(Y,Z|i-j|-V_j)
这样可以直接
但是对于此题,我们需要更快的方法:
拆开
所以,
为什么我们需要让
前面的
开两个小根堆,一个存本来少了泥土的单位泥土的
另一个存本来多了泥土的单位泥土的
得到处理当前泥土的费用后把
可以理解为后悔的贪心,把前面的费用给减去。
时间复杂度:
AC情况:https://www.luogu.com.cn/record/42168237
代码如下:
#include<bits/stdc++.h>
using namespace std;
const long long int N=100001;
long long int n,a[N],b[N],x,y,z;
long long int V[N];
long long int ans;
priority_queue<long long int,vector<long long int>,greater<long long int> > ovo,ovo2;
int main(){
scanf("%lld%lld%lld%lld",&n,&x,&y,&z);
for(long long int i=1;i<=n;i++){
scanf("%lld%lld",&a[i],&b[i]);
for(long long int o=1;o<=a[i]-b[i];o++){
V[i]=y;
if(!ovo.empty()){
V[i]=min(V[i],i*z+ovo.top());
ovo.pop();
}
ovo2.push(-V[i]-i*z);
ans+=V[i];
}
for(long long int o=1;o<=b[i]-a[i];o++){
V[i]=x;
if(!ovo2.empty()){
V[i]=min(V[i],i*z+ovo2.top());
ovo2.pop();
}
ovo.push(-V[i]-i*z);
ans+=V[i];
}
}
printf("%lld\n",ans);
}