首先考虑题目的性质,不难发现光盘的花费是一个凸函数。当生产 (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;
}