UOJ455【UER #8】雪灾与外卖【反悔贪心,模拟费用流】

给定数轴上 (n) 只老鼠和 (m) 组洞,第 (i) 只老鼠在 (x_i),第 (j) 组洞在 (y_j),有 (c_j) 个洞,代价是 (w_j)

(i) 只老鼠进第 (j) 个洞的代价是 (|x_i-y_j|+w_j),求每只老鼠都进洞的最小代价。

(2le n,mle 10^5)(0le x_i,y_i,c_i,w_ile 10^9)


模拟费用流经典题。

将所有老鼠和洞排序,并认为所有老鼠和洞都向左匹配(也即在匹配中坐标更大的位置考虑)

对老鼠和洞建堆,若当前有一只老鼠 (x),则取个最优的洞 (y) 匹配,老鼠反悔代价是 (-2x-y),而洞是不会反悔的,因为要取就取最近的。

若当前有一组洞 (y),则取个最优的老鼠 (x),洞反悔代价是 (-x-2y),老鼠反悔代价是 (-y-w)

把相同权值的元素合并,复杂度就对了!

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pii;
const int N = 100003;
template<typename T>
void rd(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; 
}
int n, m, X[N], Y[N], W[N], C[N];
LL ans, sum;
priority_queue<pii, vector<pii>, greater<pii> > ass, hole;
void pushX(int x){
	LL y = hole.empty() ? 1e14 : hole.top().fi;
	ans += x+y; ass.emplace(-2*x - y, 1);
	if(!hole.empty()){
		LL c = hole.top().se; hole.pop();
		if(c > 1) hole.emplace(y, c - 1);
	}
}
void pushY(int y, int w, int c){
	int mc = 0;
	while(mc < c && !ass.empty() && y + w + ass.top().fi < 0){
		pii _ = ass.top(); ass.pop();
		int t = min((LL)c-mc, _.se);
		ans += t * (_.fi + y + w);
		if(t != _.se) ass.emplace(_.fi, _.se - t);
		hole.emplace(-_.fi - 2*y, t); mc += t;
	}
	if(mc) ass.emplace(-y - w, mc);
	if(mc < c) hole.emplace(w - y, c - mc);
}
int main(){
	rd(n); rd(m);
	for(int i = 1;i <= n;++ i) rd(X[i]);
	for(int i = 1;i <= m;++ i){rd(Y[i]); rd(W[i]); rd(C[i]); sum += C[i];}
	if(sum < n){puts("-1"); return 0;}
	int p = 1;
	for(int i = 1;i <= n;++ i){
		for(;p <= m && X[i] >= Y[p];++ p) pushY(Y[p], W[p], C[p]);
		pushX(X[i]);
	} for(;p <= m;++ p) pushY(Y[p], W[p], C[p]);
	printf("%lld
", ans);
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/14998148.html