题解 CF802O April Fools' Problem (hard)
经典
于是可以抛开这个条件,再来看这道题。
发现我们每次决策的时候可以在打印的时候考虑,打印时考虑打印哪天准备的题目。贪心地来讲,我们一定会选择当前准备+打印总花费最少的题目。
进一步而言,对于每一次打印,我们一定会挑选之前准备花费最少且准备+打印总花费为非正数的题目进行打印。但是这显然还不够。
很显然的反例是,之前准备花费最小的题目在最优情况下,本来应该被后面的打印挑走,却被前面的打印先挑走了。这显然是不行的。
于是考虑反悔,用一个小根堆维护之前的所有决策,每次取出价格最低的决策,将当前打印的花费加上决策花费后,如果总花费为非正数,则先选择该决策。又由于:
于是把当前打印的花费的相反数加入堆中,供以后反悔使用即可。
还需要特别注意的一点是,由于 wqs 二分时应尽量多选择物品,所以当正常选的决策和反悔的决策花费相同时,我们应该优先选择正常选的决策,使得物品数更多,防止斜率相同的情况发生而无法处理。
剩下的基本就是板子了。代码很短。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
ll n,m,a[N],b[N],ans,sum,now;
struct node{
ll w;
bool pd;
node (ll w=0,bool pd=0)
:w(w),pd(pd){}
bool operator <(const node&x) const{
return (w^x.w)?w>x.w:pd<x.pd;
}
};
int read(){
int w=0,fh=1;
char ch=getchar();
while (ch>'9'||ch<'0'){
if (ch=='-') fh=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*fh;
}
bool check(ll x){
sum=now=0;
priority_queue<node> q;
while (!q.empty()) q.pop();
for (int i=1;i<=n;i++){
q.push(node(a[i]-x,1));
node s=q.top();
// printf("%lld %lld\n",1ll*i,s.w);
if (s.w+b[i]<=0){
sum+=s.w+b[i];
now+=s.pd;
q.pop();
q.push(node(-b[i],0));
}
}
return now>=m;
}
int main(){
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) b[i]=read();
ll l=0,r=2e9;
while (l<=r){
ll mid=l+r>>1;
if (check(mid)) r=mid-1,ans=sum+m*mid;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}