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;
}