最小割树:Gomory-Hu Tree
正式开始之前考虑一个问题:给定一张无向图,q次询问任意两点之间的最小割,如何实现?
最小割树
对于上面哪个问题最显而易见的做法是每次都跑一遍 dinic,时间复杂度
考虑到每次询问涉及到两个点之间的最小割,所以可以考虑建一棵树,利用树上两点之间的路径是唯一的这个性质来储存两个节点之间的最小割,因而就有了最小割树这么个东西。
最小割树的定义:
树边 (u,v) 的边权是 u,v 的最小割,删掉 (u,v) 之后树被分成的两部分是一个最小割方案中点被分成的两部分,且任意两点 S,T 之间的最小割等于 S,T 在最小割树路径上的最小值。
严格的最小割树是很难构建的,况且大多数时候我们呢并不需要知道具体的最小割方案,所以我们一般构建的都是“等价流树”,即只满足“且任意两点 S,T 之间的最小割等于 S,T 在最小割树路径上的最小值”的树。不少人都认为最小割树等价于等价流树,但实际上这是不严谨的。(具体可以参照2016年集训队论文集)。
为了方便,这里规定:
算法详解
基本性质
树上两点之间的路径上边权的最小值即为这两点之间的最小割,证明需要介绍两条引理。
引理1:对于任意三点,
proof: 根据最小割定义,引理显然成立。
引理2:假设去掉
proof:反证法,假设
,那么我们割掉所有的 a,b 之间的边之后 p,q 仍然联通,所以 a -> p -> q -> b 得出 a,b 仍然联通,与原假设矛盾,引理得证。
根据以上引理,对于两点 x,y,假设 (s,t) 为 x 到 y 路径上边权最小的边,根据定理1有
算法实现
我们用分治建树,每次在正在处理的点的区间中任选两个点 a,b,求出他们之间的最小割
代码细节
1.推荐使用namespace避免命名冲突
2.求最小边权的时候可以用树上倍增等多种方式实现
3.最开始的图要连双向边(因为你不知道流是去往哪个方向的)
实现
题目是 P4897 【模板】最小割树(Gomory-Hu Tree)
代码:
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define dub long double
const int inf = 1e9 + 7;
const int MAXN = 5100 + 10;
using namespace std;
namespace mxf{
struct edge{int u, v, f, nxt;} e[MAXN << 2];
int head[MAXN], cnt = 1;
int s, t, ans;
int cur[MAXN], dis[MAXN];
queue < int > q;
void add( int u, int v, int w ){
e[++cnt] = (edge){ u, v, w, head[u] };
head[u] = cnt;
e[++cnt] = (edge){ v, u, 0, head[v] };
head[v] = cnt;
}
bool bfs(){
memset(dis, 0, sizeof(dis));
memcpy(cur, head, sizeof(head));
dis[s] = 1;
q.push(s);
while(!q.empty( )){
int x = q.front( ); q.pop( );
for(int i = head[x]; i; i = e[i].nxt){
int y = e[i].v;
if(!e[i].f or dis[y]) continue;
dis[y] = dis[x] + 1;
q.push(y);
}
}
return dis[t];
}
int dfs(int x, int low){
if(x == t){
ans += low;
return low;
}
int used = 0, rlow = 0;
for(int &i = cur[x]; i; i = e[i].nxt){
int y = e[i].v;
if(e[i].f and dis[y] == dis[x] + 1){
if((rlow = dfs(y, min(e[i].f, low - used)))){
e[i].f -= rlow;
e[i^1].f += rlow;
used += rlow;
if(used == low) break;
}
}
}
return used;
}
void pushback(){
for(int i = 2; i <= cnt; i += 2)
e[i].f += e[i^1].f, e[i^1].f = 0;
return;
}
int dinic( ){
pushback( );
ans = 0;
while( bfs( ) ) dfs( s, inf );
return ans;
}
}
struct edge{int u, v, w, nxt;} e[MAXN << 2];
int head[MAXN], cnt;
int tmp[MAXN], node[MAXN];
int dep[MAXN];
int fa[510][15], mi[510][15];
int n, m;
void add(int u, int v, int w){
e[++cnt] = (edge){u, v, w, head[u]};
head[u] = cnt;
e[++cnt] = (edge){v, u, w, head[v]};
head[v] = cnt;
}
inline int read( ){
int x = 0 ; short w = 0 ; char ch = 0;
while( !isdigit(ch) ) { w|=ch=='-';ch=getchar();}
while( isdigit(ch) ) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return w ? -x : x;
}
void work(int l, int r){
if(l >= r) return;
int cnt1 = l, cnt2 = r;
int x = node[l], y = node[l + 1];
mxf :: s = x; mxf :: t = y;
int cut = mxf :: dinic( );
add(x, y, cut);
for(int i = l; i <= r; i++)
if(mxf :: dis[node[i]]) tmp[cnt1++] = node[i];
else tmp[cnt2--] = node[i];
for(int i = l; i <= r; i++)
node[i] = tmp[i];
work(l, cnt1 - 1);
work(cnt2 + 1, r);
}
void dfs(int x, int f){
dep[x] = dep[f] + 1;
fa[x][0] = f;
for(int i = 1; i <= 8; i++){
fa[x][i] = fa[fa[x][i-1]][i-1];
mi[x][i] = min(mi[x][i-1], mi[fa[x][i-1]][i-1]);
}
for(int i = head[x]; i; i = e[i].nxt){
int y = e[i].v;
if(y == f) continue;
mi[y][0] = e[i].w;
dfs(y, x);
}
}
int getcut(int x, int y){
int res = inf;
if(dep[x] < dep[y]) swap(x, y);
for(int i = 8; i >= 0; i--)
if(dep[fa[x][i]] >= dep[y]){
res = min(res, mi[x][i]);
x = fa[x][i];
}
if(x == y) return res;
for(int i = 8; i >= 0; i--){
if(fa[x][i] != fa[y][i]){
res = min(res, min(mi[x][i], mi[y][i]));
x = fa[x][i]; y = fa[y][i];
}
}
res = min(res, min(mi[x][0], mi[y][0]));
return res;
}
signed main( ){
n = read( ); m = read( );
for(int i = 1; i <= m; i++){
int u = read( ), v = read( ), w = read( );
mxf :: add(u, v, w);
mxf :: add(v, u, w);
}
for(int i = 0; i <= n; i++)
node[i] = i;
work(0, n);
dfs(1, 0);
int q = read( );
for(int i = 1; i <= q; i++){
int x = read( ), y = read( );
printf("%lld\n",getcut(x, y));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具