洛谷P3049 [USACO12MAR]园林绿化Landscaping

https://www.luogu.org/problem/show?pid=3049
这道题的思路啦,其实还好啦;
但是自己想的时候跟傻逼一样;
还好又fop_zz给我讲了半天;
做这道题目啦,先去做一下洛谷的一题“种树”
这道题目啦
最本质的思路,就是先计算当前最优答案,加入总答案,然后在之后的计算里,来更新之前所谓的“最优答案”
比如我们现在在i缺一
那我们花费x的钱补一
之后我们在j多一颗土
那么我们假如这个土移到i
那当前的答案是不是(j-i)*z-x
当然我们也可以直接运走费用y
那额当前最优解就是min((j-i)*z-x,y)
之后我们枚举到k又缺了土
假如从j移动到k
那么答案是不是(k-j)*z-min((j-i)*z-x,y)
当然这个还要和x取一个min

那我们来观察从i移到j的转移
(j-i)*z-x
=jz-iz-x
=jz-(iz+x)
这里的iz其实和j是没有什么关系的
这里的x就是在i的时候的最优解
那么我们是不是在j的时候对之前所有缺土的点取一个max(iz+x)就是当前最优解了
这个是不是一个堆就可以完成了?
所以我们维护2个堆,一个是多余,一个数不足
那么我们算出当前的最优解的时候,把答案放到另一个堆里面,假如现在的最优解并不是最终答案,那么就可以通过这个堆体现出来,并且在后面的“当前最优解”里面减去这个错误的最优解,最终保持当前的正确答案
这也是为什么a[i],b[i]的值最大只有10

#include
#define Ll long long
using namespace std;
const Ll N=1e5+5;
Ll n,x,y,ans,m1,m2,z;
priority_queueQ1;
priority_queueQ2;
int main()
{
    scanf("%lld%lld%lld%lld",&n,&x,&y,&z);
    for(Ll j=1;j<=n;j++){
        scanf("%lld%lld",&m1,&m2);
        if(m1for(Ll i=1;i<=m2-m1;i++)
                if(Q1.empty()||j*z-Q1.top()>=x){
                    ans+=x;Q2.push(x+j*z);
                }else{
                    Ll v=Q1.top();Q1.pop();
                    ans+=j*z-v;Q2.push(j*z*2-v);
                }
        else
            for(Ll i=1;i<=m1-m2;i++)
                if(Q2.empty()||j*z-Q2.top()>=y){
                    ans+=y;Q1.push(y+j*z);
                }else{
                    Ll v=Q2.top();Q2.pop();
                    ans+=j*z-v;Q1.push(j*z*2-v);
                }
    }
    printf("%lld",ans);
}

你可能感兴趣的:(堆,____数学数论____)