1. 程式人生 > 實用技巧 >UOJ455 雪災與外賣(模擬費用流)

UOJ455 雪災與外賣(模擬費用流)

UOJ455 雪災與外賣(模擬費用流)

題目大意

給定一些老鼠,所有老鼠都要進洞,每個洞有容量限制和每進一隻老鼠的代價,同時每個老鼠和洞都有座標,老鼠走到洞還要付出距離的代價,最小化總代價

資料範圍

\(1 \le n \le 10^5\)

解題思路

推薦大佬部落格,因為學的他的,所以或許很相似

這應該可以說是模擬費用流的經典題

先考慮一些簡單的問題

下面預設所有座標有序

模型一

所有老鼠向左走,每個洞裡進一隻,代價是距離,所有老鼠都進洞

顯然直接貪心即可,維護一個棧,每次老鼠去最近的

模型二

現在所有老鼠不一定進洞,並且每隻老鼠進洞會付出 \(w1[i]\) 的代價,每隻洞被進會付出 \(w2[i]\)

的代價

注意這裡的代價可負

老鼠 i 進洞 j 的代價是 \(w1[i]+w2[j]+x[i]-x[j] = (x[i]+w1[i])-(w2[j]+x[j])\)

顯然我們用個堆維護即可

模型三

模型二的基礎上必須全部進洞,那麼直接把代價都減去 inf 最後在加上即可

模型四

模型二的基礎上左走右走均可,這個問題就複雜些了

從左到右模擬費用流,我們在後面加入一個老鼠或洞並用反悔(退流)操作維護當前最優解

維護兩個堆 \(q1, q2\),分別表示待匹配老鼠和洞

如果當前是老鼠,那麼有三種操作

  • 等待後面的洞和它匹配

  • 和前面未匹配的洞匹配

  • 搶前面老鼠的洞

如果當前是洞,那麼也有三種操作

  • 等待後面的老鼠和它匹配
  • 和前面未匹配的老鼠匹配
  • 搶前面洞的老鼠

我們可以把後面的兩個操作合併起來做

我們考慮洞的情況,那麼老鼠的情況也將迎刃而解

第一個操作是直接把 \(w[i]-x[i]\) 加入堆中

第二三個操作是和老鼠發生匹配,那麼以後有可能有老鼠來搶洞或者有洞來搶老鼠,我們需要提前考慮這兩種情況

有老鼠來搶洞,那麼我們新增一個代價是 "\(-k-2x[i]\)" 的洞

有洞來搶老鼠,那麼我們新增一個代價是 "-w[i]-x[i]" 的老鼠

一個小細節是如果一個洞搶了老鼠,那麼原先的洞不可能再被後面的老鼠搶到,因為老鼠和洞的連線時不可交叉的

模型四

加上容量和必選的條件,我們也可以輕鬆解決,必選直接在代價減去 inf,容量我們用 pair 儲存即可,可能細節會多一些,這就是雪災和外賣了

程式碼

#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template<typename F>
inline void write(F x, char ed = '\n')
{
	static short st[30];short tp=0;
	if(x<0) putchar('-'),x=-x;
	do st[++tp]=x%10,x/=10; while(x);
	while(tp) putchar('0'|st[tp--]);
	putchar(ed);
}

template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }

template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }

const int N = 200500;
const int inf = 1e9;
struct node {
	ll y, w, c; 
}p[N];

struct Pair {
	ll x, y;
	Pair(int xx = 0, int yy = 0) : x(xx), y(yy) {}
	bool operator < (const Pair t) const {
		return x == t.x ? y > t.y : x > t.x;
	}
};

priority_queue<Pair> q1, q2;

ll ans, x[N], res, m, n;
int main() {
//	freopen ("hs.in","r",stdin);
	read(n), read(m);
	for (int i = 1;i <= n; i++) read(x[i]);
	for (int j = 1;j <= m; j++) 
		read(p[j].y), read(p[j].w), read(p[j].c), res += p[j].c, p[j].w -= inf;
	if (res < n) return puts("-1"), 0; 
	int j = 1; x[n + 1] = 1e9;
	for (int i = 1;i <= n + 1; i++) {
		while (j <= m && p[j].y <= x[i]) {
			ll sum = 0; 
			while (p[j].c && q1.size()) {
				Pair t1 = q1.top(); if (t1.x + p[j].w + p[j].y >= 0) break;
				ll lim = min(p[j].c, t1.y); q1.pop();
				ans += lim * (t1.x + p[j].w + p[j].y), sum += lim;
				p[j].c -= lim, t1.y -= lim; if (t1.y) q1.push(t1);
				q2.push(Pair(-t1.x - 2 * p[j].y, lim));
			}
			if (p[j].c) q2.push(Pair(-p[j].y + p[j].w, p[j].c));
			if (sum) q1.push(Pair(-p[j].y - p[j].w, sum)); j++;
		}
		if (i > n) break;
		if (q2.size()) {
			Pair t2 = q2.top(); 
			if (t2.x + x[i] >= 0) {
				q1.push(Pair(-x[i], 1));
				continue;
			}
			q2.pop(), ans += t2.x + x[i], t2.y--;
			if (t2.y) q2.push(t2);
			q2.push(Pair(-x[i], 1));
			q1.push(Pair(-t2.x - 2 * x[i], 1));
		}
		else q1.push(Pair(-x[i], 1));
	}
	ans += (ll)n * inf, write(ans);
	return 0;
}