我觉得这东西有必要记一下,因为光是看 PPT 很难自己写出代码……具体步骤相关啥都没写。
另外学这个东西也不是很必要……
Solution
我们需要一个维护最小值、最小值编号,支持区间加的 LCT。需要支持以下操作:
- \(find\_root(u)\)
- \(link(u,v)\)
- \(cut(u,v)\)
- \(find\_min(v)\)
- \(add(u,value)\)
(注意到只需要实现 access)
首先 BFS 步骤不变,注意从 \(t\) 倒着来。得到分层图 \(G_L\) 后,通过 DFS 增广,增广步骤如下:
- 对 \(G_L\) 上的每一个点建立一棵恰好包含自己一个点的树。
- 设跟踪的节点为 \(v\),初始置 \(v\) 为源点 \(s\)。选择一条出边 \((v,u)\),将 \(v\) 所在树连接到 \(u\) 上,设 \(v\) 的权值为出边的边权,最后将 \(v\) 移动到当前树的根。
- 若 \(v\) 没有出边,那么回溯到 \(u\),删除 \((u,v)\),如果该边是树边,则断开树上相应边。
- 如果 \(v=t\),找到树上对应的 \(s\sim t\) 路径,将总流量加上 \(find\_min(s)\),再将路径上所有点的权值减去增广流量,删除路径上的所有零边。重新置 \(v\) 为源点 \(s\),重复步骤 3。
- 如果源点 \(s\) 没有出边,所有增广结束,进行下一次分层。
Code
#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
#define mem(a) memset(a, 0, sizeof(a))
typedef long long ll;
const int N = 1e5 + 10, M = 1e5 + 10, INF = 2147483647;
int n, m, s, t;
struct Edge {int t, n, w, del;
} e[M];
int tot = 1, h[N], cur[N];
void add(int u, int v, int w) {e[++tot] = (Edge){v, h[u], w, 0}, h[u] = cur[u] = tot;
}
void eadd(int u, int v, int w) {add(u, v, w), add(v, u, 0);
}struct LCT {int ch[N][2], fa[N], mn[N], mnid[N], add[N], val[N], id[N], lef[N];LCT() {mem(ch), mem(fa), mem(add), mem(id);}#define get(u) (u == ch[fa[u]][1])#define nrt(u) (u == ch[fa[u]][0] || u == ch[fa[u]][1])void up(int u) {mn[u] = min({mn[ch[u][0]], val[u], mn[ch[u][1]]});if (mn[u] == mn[ch[u][0]]) mnid[u] = mnid[ch[u][0]];else if (mn[u] == val[u]) mnid[u] = u;else mnid[u] = mnid[ch[u][1]];if (ch[u][0]) lef[u] = lef[ch[u][0]];else lef[u] = u;}void Add(int u, int v) {if (u) mn[u] += v, add[u] += v, val[u] += v, e[id[u]].w += v, e[id[u] ^ 1].w -= v;}void down(int u) {if (add[u]) Add(ch[u][0], add[u]), Add(ch[u][1], add[u]), add[u] = 0;}void Down(int u) {if (nrt(u)) Down(fa[u]);down(u);}void rot(int u) {int f = fa[u], g = fa[f], k = get(u);if (nrt(f)) ch[g][get(f)] = u;ch[f][k] = ch[u][!k];if (ch[u][!k]) fa[ch[u][!k]] = f;ch[u][!k] = f, fa[f] = u, fa[u] = g, up(f), up(u);}void splay(int u) {for (Down(u); nrt(u); rot(u)) if (nrt(fa[u])) rot(get(u) == get(fa[u]) ? fa[u] : u);}void access(int u) {for (int v = 0; u; v = u, u = fa[u]) splay(u), ch[u][1] = v, up(u);}int find(int u) {return access(u), splay(u), lef[u];}void Del0(int u) {for (down(u); ch[u][1] && !mn[ch[u][1]]; )u = mnid[ch[u][1]], splay(u), fa[ch[u][0]] = 0, ch[u][0] = 0, up(u);}void cut(int u) {access(u), splay(u), fa[ch[u][0]] = 0, ch[u][0] = 0, up(u);}#undef get#undef nrt
} lct;void init() {rep(i, 1, n) lct.splay(i); // 释放标记lct = LCT();rep(i, 0, n) lct.mnid[i] = lct.lef[i] = i, lct.mn[i] = lct.val[i] = INF;
}int res, dis[N], que[N];
int bfs() {memset(dis, 0, sizeof(dis));init();dis[que[1] = t] = 1;for (int L = 1, R = 1; L <= R; ++L)for (int i = h[que[L]]; i; i = e[i].n) {e[i].del = e[i ^ 1].del = 0;if (e[i ^ 1].w && !dis[e[i].t])dis[que[++R] = e[i].t] = dis[que[L]] + 1;}if (!dis[s]) return 0;memcpy(cur, h, sizeof(cur));return 1;
}int dfs(int u) {if (u == t) {lct.access(s), lct.splay(t);int ret = lct.mn[t];res += ret;lct.add[t] -= ret;lct.Del0(t);return 1;}for (int &i = cur[u]; i; i = e[i].n) {int v = e[i].t;if (e[i].w && dis[v] == dis[u] - 1 && !e[i].del) {lct.splay(u), lct.fa[u] = v, lct.val[u] = e[i].w, lct.id[u] = i, lct.up(u);if (dfs(lct.find(u))) return 1;}}for (int i = h[u]; i; i = e[i].n) {int v = e[i].t;if (dis[v] == dis[u] + 1) {if (lct.find(v) == u)lct.cut(v);e[i ^ 1].del = 1;}}return 0;
}int main() {ios::sync_with_stdio(false), cin.tie(nullptr);cin >> n >> m >> s >> t;rep(i, 1, m) {int u, v, w;cin >> u >> v >> w;eadd(u, v, w);}while (bfs()) while (dfs(lct.find(s)));cout << res << '\n';return 0;
}