传送门:bzoj4977
题解
很妙的一个贪心
选择 ( x , y ) (x,y) (x,y)(x降落到y)的代价是 a x − b y + c y a_x-b_y+c_y ax−by+cy
如果没有一个房子只能容纳一个人的限制,就是维护 a , b a,b a,b前缀大小的堆,每次弹出栈顶的 − b i + c i -b_i+c_i −bi+ci,代价加上 a x − b i + c i a_x-b_i+c_i ax−bi+ci(若堆为空或贡献 ≤ 0 \leq 0 ≤0则不操作)
考虑存在 x , z ( x < z ) x,z(x<z) x,z(x<z), ( x , y ) (x,y) (x,y)是最优选择,但实际上 ( z , y ) (z,y) (z,y)更优,类似于费用流的退流操作,将 z z z所容纳的由 x x x改为 z z z,多了 a z − a x a_z-a_x az−ax的贡献,所以在 x x x弹出堆顶时,堆中加入 − a x -a_x −ax表示修改。这样贪心正确性就没有问题了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m;ll ans;
struct node{int x,c;}d[N<<1];
inline bool cmp(node A,node B) {return A.x==B.x?A.c<B.c:A.x<B.x;}
priority_queue<int> q;
int main(){
int i;scanf("%d%d",&n,&m);
for(i=1;i<=n;++i) scanf("%d",&d[i].x),d[i].c=-1;
for(i=1;i<=m;++i) scanf("%d%d",&d[n+i].x,&d[n+i].c);
n+=m;sort(d+1,d+n+1,cmp);
for(i=1;i<=n;++i) {
if(d[i].c==-1) {
if(q.empty() || q.top()+d[i].x<=0) continue;
ans+=q.top()+d[i].x;
q.pop();q.push(-d[i].x);
}else q.push(d[i].c-d[i].x);
}
printf("%lld",ans);
return 0;
}