算法
最小割树是一个用于快速求无向图上任意两点间最小割的算法,它的定义如下:
称树 T T T 为 无向图 G G G 的最小割树,当且仅当 T T T 的节点与 G G G 一一对应,且对于所有边 ( u , v ) ∈ T (u,v) \in T (u,v)∈T,其边权为原图中 u , v u,v u,v 两点间最小割,同时满足在 T T T 中去掉这条边后剩下的两个不连通点集恰好为原图中 u , v u,v u,v 的最小割将原图分为的两个点集。
显然,我们可以直接根据定义递归建树,并利用最小割最大流定理每次跑一边网络流,代码:
void build(int l,int r){
//node[l]到node[r]为当前要处理的连通点集
if(l == r) return;
int u = node[l],v = node[l + 1];//任选两点
add(u,v,Max_Flow:: Dinic(u,v));
int t1 = l - 1,t2 = r + 1;
for(int i = l; i <= r; i ++){
if(Max_Flow:: d[node[i]] == -1) t[--t2] = node[i];
else t[++t1] = node[i];
//d为Dinic的bfs中的到达数组,显然此时d中存的是最后一次bfs时的残量网络的连通情况
//因为现在残量网络上s到达不了t,所以bfs访问到的点就是s所在的点集,它们的d不为0
}
for(int i = l; i <= r; i ++) node[i] = t[i];
build(l,t1),build(t2,r);//node[l]到node[t1]为u所在点集,node[t2]~node[r]为v所在点集
}
那建出树后怎么查询呢?最小割树有一个重要的性质:
对于原图中任意两点 u , v u,v u,v,其最小割等于树上两点路径上边权的最小值
所以直接倍增维护就可以了。
证明
其正确性证明可以去看这篇博客,写的很严谨。
关于时间复杂度
严格来说,其时间复杂度上界约为 O ( n 3 m ) \Omicron(n^3m) O(n3m)(共跑 n − 1 n -1 n−