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 = '
')
{
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;
}