题解 洛谷 P4694 【[PA2013]Raper】

首先考虑题目的性质,不难发现光盘的花费是一个凸函数。当生产 (0) 张光盘时,其花费为 (0),随着光盘生产数的增加,最优情况肯定是先选择工厂便宜的时刻,所以花费会增长越来越快,因此其为一个下凸的凸函数。

采用 (WQS) 二分来优化掉生产出 (k) 张光盘的限制,然后可以通过二分图带权匹配来判定。每个 (b) 向其之前所有的 (a) 连边,表示可以进行匹配来生产光盘,当匹配的权值为正时就停止匹配,用匹配数来判定二分。

但是这样复杂度无法接受,于是采用模拟费用流的方法,用一个小根堆来实现反悔操作,堆中为 (a) 的权值,每次 (b) 和最小的 (a) 进行匹配。但是这样匹配不一定是最优,因此就像费用流一样,将 (b) 的权值取负再加入堆中,表示可以有别的 (b) 来代替它。

判定时二分的权值加在 (b) 上,和 (b) 一同取负即可,同时还需注意每次匹配是产生新匹配还是代替之前的匹配。

(code:)

#include<bits/stdc++.h>
#define maxn 500010
#define inf 10000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
ll n,k,ans,cnt,sum,l=-inf,r=inf;
ll a[maxn],b[maxn];
struct node
{
    ll val;
    bool tag;
};
bool operator <(const node &a,const node &b)
{
    return a.val>b.val;
}
bool check(ll x)
{
    priority_queue<node> q;
    cnt=sum=0;
    for(int i=1;i<=n;++i)
    {
        q.push((node){a[i],0});
        if(q.top().val+b[i]+x<=0)
        {
            sum+=q.top().val+b[i]+x;
            if(!q.top().tag) cnt++;
            q.pop(),q.push((node){-b[i]-x,1});
        }
    }
    return cnt>=k;
}
int main()
{
    read(n),read(k);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=n;++i) read(b[i]);
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    check(ans),printf("%lld",sum-ans*k);
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/13335837.html