【题解】P5192 有源汇上下界最大流
题目大意
Aya超可爱的~ 题目传送门
题意非常清晰:共有若干组数据
文文第
现在求文文
题解
(前置知识:网络流最大流。)
虽然题目要求有源汇有上下界的最大流,但先让我们考虑无源汇有上下界可行流。
无源汇有上下界可行流
它的定义非常简单:一张
既然每个点都有上下界,那么我们先满足它的下界好了。即对于每条边先填充
每个点有一个权值
如果我们再改一下题面:有一个超级源点
让我们重新梳理一下上述内容:我们强制给每条边添上了一些流,或者说将这条边的流量范围变为了
因为对于每条边
但是先别高兴,我们要解决的是有源汇有上下界可行流。
有源汇有上下界可行流
有源汇这个条件给了我们什么东西呢?他给了我们源点
其实,我们可以通过连接一条边
有源汇有上下界最大流
但是我们的问题是要求最大流呀!不要慌,至少我们已经找到了一种合法的情况了。最大流算法需要扩大答案怎么办?在残量网络上跑增广路。而有源汇有上下界最大流也不例外(因为我们实际上已经把问题转化为了最大流问题,不过是多出了两个特殊点
让我们重新回忆一下增广路的概念:从点
由于
建图
说了那么多,应该回到题目了。记
总结:这条题目的流程为读入边,处理边,跑普通版最大流。因此编写难度并不是很大。
扩展:关于最高标号预留推进(HLPP)
很显然,上面利用超级源汇点疏通边权达到进出平衡的算法可以用
不过,由于使用 HLPP 算法有点大炮打蚊子了,所以这里直接使用的
参考代码
#include <bits/stdc++.h>
using namespace std;
int qread(){
int w = 1, c, ret;
while((c = getchar()) > '9' || c < '0') w = (c == '-' ? -1 : 1); ret = c - '0';
while((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + c - '0';
return ret * w;
}
using i64 = long long;
const int INF = 1e9;
const i64 INFL = 1e18;
namespace MCMF{
const int MAXN = 1e5 + 3;
const int MAXM = 2e5 + 3;
int H[MAXN], V[MAXM], N[MAXM], F[MAXM], o = 1, n;
void add0(int u, int v, int f){
V[++ o] = v, N[o] = H[u], H[u] = o, F[o] = f;
V[++ o] = u, N[o] = H[v], H[v] = o, F[o] = 0;
n = max(n, u);
n = max(n, v);
}
i64 D[MAXN];
bool bfs(int s, int t){
queue <int> Q;
for(int i = 1;i <= n;++ i)
D[i] = 0;
Q.push(s), D[s] = 1;
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int i = H[u];i;i = N[i]){
const int &v = V[i];
const int &f = F[i];
if(f != 0 && !D[v]){
D[v] = D[u] + 1;
Q.push(v);
}
}
}
return D[t] != 0;
}
int C[MAXN];
i64 dfs(int s, int t, int u, i64 maxf){
if(u == t)
return maxf;
i64 totf = 0;
for(int &i = C[u];i;i = N[i]){
const int &v = V[i];
const int &f = F[i];
if(f && D[v] == D[u] + 1){
i64 f = dfs(s, t, v, min(1ll * F[i], maxf));
F[i ] -= f;
F[i ^ 1] += f;
totf += f;
maxf -= f;
if(maxf == 0){
return totf;
}
}
}
return totf;
}
i64 mcmf(int s, int t){
i64 ans = 0;
while(bfs(s, t)){
memcpy(C, H, sizeof(H));
ans += dfs(s, t, s, INFL);
}
return ans;
}
int G[MAXN];
void add(int u, int v, int l, int r){
G[v] += l;
G[u] -= l;
add0(u, v, r - l);
}
void clear(){
for(int i = 1;i <= o;++ i){
N[i] = F[i] = V[i] = 0;
}
for(int i = 1;i <= n;++ i){
H[i] = G[i] = C[i] = 0;
}
o = 1, n = 0;
}
bool solve(){ // 无源汇
int s = ++ n;
int t = ++ n;
i64 sum = 0;
for(int i = 1;i <= n - 2;++ i){
if(G[i] < 0)
add0(i, t, -G[i]);
else
add0(s, i, G[i]), sum += G[i];
}
auto res = mcmf(s, t);
if(res != sum)
return true;
return false;
}
i64 solve(int s0, int t0){ // 有源汇
add0(t0, s0, INF);
int s = ++ n;
int t = ++ n;
i64 sum = 0;
for(int i = 1;i <= n - 2;++ i){
if(G[i] < 0)
add0(i, t, -G[i]);
else
add0(s, i, G[i]), sum += G[i];
}
auto res = mcmf(s, t);
if(res != sum)
return -1;
return mcmf(s0, t0);
}
}
const int MAXN = 1e3 + 3;
const int MAXM = 365 + 3;
int G[MAXN], A[MAXN], B[MAXM];
int main(){
ios :: sync_with_stdio(false);
cin.tie(nullptr);
int n, m, o = 0;
while(cin >> n >> m){
int s = ++ o;
int t = ++ o;
for(int i = 1;i <= m;++ i){
cin >> G[i];
A[i] = ++ o;
MCMF :: add(A[i], t, G[i], INF);
}
for(int i = 1;i <= n;++ i){
B[i] = ++ o;
int c, d;
cin >> c >> d;
MCMF :: add(s, B[i], 0, d);
for(int j = 1;j <= c;++ j){
int t, l, r;
cin >> t >> l >> r;
t ++;
MCMF :: add(B[i], A[t], l, r);
}
}
cout << MCMF :: solve(s, t) << "\n\n";
MCMF :: clear();
}
return 0;
}
后记
明明说好的是模板题,然而并不是赤裸裸的板题(虽然一眼就能看出来是板子),所以调试起来挺麻烦的。但只要你有足够扎实的最大流功底,这条题目对于你就是小菜一碟。
另外,要注意少女的下标从
其实我做这条题目的动机完全是因为文文